Jeff Bailey
Written by Jeff Bailey

Learning resources, opinions, and facts about technology.

A Software Development Philosophy

Reading Time: 24.8 minutes
Listen to this post

A software development philosophy guides developers throughout their careers. Some adhere to prevailing philosophies written down in books or code, and some invent their own based on their software worldview.

My philosophical influences come from lean Domain-Driven Design (DDD), Test-Driven Development (TDD), The Agile Software Development Lifecycle, Developer Operations (DevOps), and many others.

Here is my software development philosophy. I encourage you to write your own and join the conversation.

Invest Lightly

I avoid getting too wrapped up in details when using various technologies. Technology changes constantly; light and strategic investments provide more long-term value. I invest lightly with my keystrokes, so I maintain less code.

Stay Liquid

I avoid technical debt like the plague.

It’s frighteningly common to witness million-dollar software development projects scrapped and built for millions again. A technical debt mountain is no laughing matter; it will threaten the longevity of your business. I recommend paying your technical debt now so you don’t pay more later. It is more often cheaper to pay technical debt than rewrite software.

Everything Changes

When building code, I assume change is coming and sooner than expected. I craft easy-to-adjust solutions that economically address new business requirements.

Simple Security

Simple security is often an oxymoron, but there are solutions.

I create layers like bastion hosts to protect data rather than software configuration files and leave configuration files permissive while mostly unchanged to reduce confusion about access rules.

I at least require clients to identify themselves before calling my services. Legacy deprecation is hard enough without playing a game of whack-a-mole or running scream tests during migration activities.

I often remind myself that the only secure computer is a dead computer, and you better be sure it’s demolished, in the ocean, encased in cement, and buried.

Use the Right Tool

Languages are simply programming tools that exist to help us craft a solution. I contemplate the appropriate tool for a job and am acutely aware of The Law of Instrument.

I prefer learning many tools to support full-stack development proficiency. In software development, many people use one tool and master it. Specializing is lucrative; some developers will land a big payoff with this approach. However, I prefer to follow the 80-20 rule when educating myself.

The Mythical Man-Month

Many people look outside software development efforts and believe adding more people will speed up a project. I don’t blame them; it seems like the logical outcome; however, it’s never true on a short timeline.

I’m acutely aware of Brook’s Law. Adding more people to a team will initially slow it down, and large teams are inherently slower.

I’m pragmatic when adding new team members and continuously streamline onboarding to lower the impact of adding new team members.

Measure Then Commit

I know that everyone makes mistakes and that limiting errors is critical. When building software, I focus on critical decisions first, then fire tracer bullets to determine options. Once options are defined, I will pass a proposal by my team or business stakeholders to eliminate blind spots.

I love resolving problems that positively impact people’s lives. I live for this.

All The Logs In The Forest

If there’s a log, I’ll enable it. When I’m stuck debugging a problem, I never know what logs might uncover, so I enable all of them.

While information is essential, too much information drives me crazy. I appropriately classify logs with DEBUG, INFO, WARN, ERROR, and FATAL.

I use general-purpose logging solutions with aspect-oriented programming and move logging concerns away from the application layer.

A Fresh Start

When I pick up a dusty software development tool, I approach my work as a newbie. I’ll Google for an appropriate modern tool and remind myself that the tool I’m using now may now be worthless in our fast-paced industry.

I use principles to guide my efforts rather than my vast knowledge of a particular tool.

Solutions Looking For a Problem

I avoid creating solutions before solving a problem. I ask myself a simple question before building anything.

Does solving this problem add significant value?

If a solution to a problem isn’t providing significant value, I save the idea for later and focus on high-value solutions.

Use Cases

It is vital to limit software use cases. Command-line utilities are easy to restrict use cases while user interfaces are not. I limit use cases to avoid introducing a diverse collection of bugs.

Writing down a clear definition of use cases sets a clear contract between myself and users. When people use software outside of an intended use case, I use documentation to explore a new valid use case.

If I am explicitly creating general-purpose software, I limit the use cases as much as possible to general-purpose features that developers can extend.

Constraints

If it’s flapping around, I nail it down. I identify and document constraints before writing any code when tackling significant problems. Defining constraints limits choices and helps narrow a solution path.

I pay particularly close attention to resource constraints and the price associated with those resources. Before implementing an architecture, I’ll review the constraints to discover blockers or bottlenecks.

I look for opportunities to simplify business problems. I regularly validate previously defined constraints. If something changes in my favor, I’ll forge a new path to simplicity.

Versioning

When building software, my philosophy is to version most things. I abide by the rule; if it’s not broken, don’t fix it. I keep versions pinned, so upgrades don’t happen without a developer’s knowledge and strike a balance between updating software for security reasons and letting sleeping dogs lie.

Naming Things

When naming variables, I name them in ways that make them easy to rename. I call things exactly and avoid overloading or overusing the same variable name.

Tracer Bullets

I use tracer bullets to punch a hole in the walls between myself and a solution. I’ll use scattershot shotgun methods to prove use cases, theories, and the viability of a solution before investing time and money in building robust solutions. I’ll pick a requirement far away from the end solution and meet it head-on.

There Are No Big Problems

As Henry Ford once said, there are just a lot of minor problems. Breaking down what looks like big problems into manageable problems makes software developers look like wizards. 🧙‍♂️

If I face a complex problem, I find a minor problem I can solve and work from there.

Driving Business Value

Everything I build has the end-user requirements placed front and center in my mind. Having a fancy contract with a lot of complexity won’t help sell more products than a simple one. If I can’t rationalize why a feature provides the highest value at a given time, I’ll raise my hand or change course on my own.

Single Responsibility

I keep software components isolated with a single responsibility and create user interfaces with a single responsibility to reduce complexity. I keep tools divided logically for users and other software developers, and I follow SOLID principles when creating object-oriented software solutions. I believe that many software problems are caused by not following this principle.

Take Responsibility

Development Operations (DevOps) has transformed software development. Modern software development practices have greatly benefited from the idea of DevOps. DevOps requires that software development teams take full responsibility for the products they create. The DevOps maturity model increases the quality of products and increases the speed of delivery to the market.

I am fully bought into this philosophy and work to push team members towards continuous improvement of build pipelines, testing methodologies, and the other DevOps best practices.

There Is Always A Design

Designing a maintainable, malleable, and easy-to-understand system is of paramount importance. A design always exists. An unplanned design is terrible, but it is still a design. A poor design becomes a massive liability for a company and will slow a development team to a crawl.

I insist on a good design upfront. My primary goals include making a system sufficiently flexible, easy to understand, and low in long-term cost.

Be Empathetic

I work diligently to understand what people need to do with the software I build, and I ask why five times to understand the real problems and keep the big picture in mind. Empathy for people is a cornerstone of a robust software development philosophy.

Step Slowly

When dealing with complex problems, I step slowly through the code. Slowly stepping through code helps me isolate issues and solve them one at a time. I’ll make a single change and then use a debugger to see the results.

Automate Everything

I automate tasks religiously. If it’s hard to automate something and it will cost a fortune to automate, I consider a different solution.

Test For Life

I like focusing on solving problems. Without robust integration tests, unit tests, and any other test, we spend time creating problems for ourselves.

I write tests because I don’t like manually running an app I’m working on after every change. An automated test suite helps me sleep at night, knowing that I build top-notch products. Time spent coding without testing increases the time spent working on QA errors, investigating production issues, and troubleshooting broken builds. Doing those things are prevented by testing; therefore, I test for life.

Decouple Everything

I refactor tightly coupled code to reduce complexity and prepare it for change. When tight couplings exist for no good reason, I will decouple them. I summarily dismiss software libraries that aren’t dependency injection-ready.

I ask myself five times why I’m coupling code before committing to tight coupling.

No Perfect Solutions

I know that picking the best solution, at the moment, with all the facts laid out, is the way to avoid analysis paralysis.

I don’t believe in the Nirvana fallacy and make the best decision possible at The Last Responsible Moment, yet at the same time, I take big architectural choices very seriously.

I know that solutions can’t be fast, cheap, and good simultaneously, yet I strive for the right balance based on functional requirements.

Conway’s Law Is True

Conway’s Law states that the organization’s communication structure influences a system design. If the communication flows are complex, the systems will also be complicated. I look at the flow of information in a company to help me understand its procedures. If the flow of company information is inefficient, I will propose a better solution.

Any organization that designs a system (defined broadly) will produce a design whose structure is a copy of the organization’s communication structure.

Melvin E. Conway

Open Source is Life

I make use of open-source software whenever possible. I make sure the open-source software I use is well maintained, and I make safe bets on a project’s longevity. I’ll contribute to a project if it aligns with my development needs and enjoy the synergies that open-source software development models provide to businesses and developers.

Can != Should

The software development world is full of many tools, methods, design patterns, and best practices. Frequently choice overload leads software developers to believe one tool can rule them all. 💍

Picking the wrong tools to solve your problem leads to technical debt and wasted time. Before tackling a problem, I will research what standard methods are used and choose the best tools for a specific situation.

Confusion Is Painful

Software development is overly complicated as-is. I follow the principle of least astonishment when crafting solutions. If I’m taking an unconventional path, I prepare a steelman argument for doing so, keep the code simple, and document why I created a new approach.

I keep my variables consistent, methods clear, configuration files clean, and sound choices that eliminate inconsistencies within a given solution.

Inconsistency is an enemy of excellent software design.

Pair Programming FTW

I enjoy a productive pair programming session. I love sharing my hard-earned wisdom. Programming in the modern age is a self-induced chaos test meant to push my ability to understand EVERYTHING to the limit. When I help someone avoid the pain I’ve experienced, it brings me joy.

Another pair of eyes will make code easier to maintain and lower the total cost of ownership. Pair programming, when done right, will level up your team while turning it into a high-performing powerhouse.

A Software Development Philosophy Evolves

My software development philosophy is evergreen. It changes after learning new things. This account of my development philosophies is nowhere near complete, and it never will be. As always, a great magician never reveals all his tricks.

I refine my software development philosophy regularly in the spirit of continuous improvement.

Develop your philosophy

Here are a few books that will help you develop your software development philosophy.