Listen to this post
A software development philosophy guides every developer throughout their career. Some adhere to prevailing philosophies that are written down in books or written down in code. Some invent their own beliefs based on their view of the software world.
My philosophical influences come from the likes of Domain-Driven Design, Test Driven Development, the Agile Software Development Lifecycle, and a multitude of others.
The following prose is an account of my software development philosophy.
When using distinct technologies, I try not to get wrapped up into the details. Technology changes constantly; light strategic investments provide more long-term value. I invest lightly with my keystrokes, so I have less code to maintain down the road.
I avoid technical debt like the plague. A technical debt mountain is no laughing matter; it will threaten the longevity of your business. I recommend paying the debt now, so you don’t pay more later. It’s frighteningly common to see million-dollar software development projects get scrapped and built for millions again. It’s more often cheaper to pay technical debt than it is to rewrite software.
When building code, I assume change is coming and sooner than expected. I craft solutions that are easy to change in the future to more economically address new business requirements.
I create layers like bastion hosts to protect data rather than using software configuration files and leave configuration files permissive to reduce confusion about access rules. I often remind myself that the only secure computer is a dead computer, and you better be sure it’s dead.
Use the Right Tool
Languages are simply programming tools that exist to help us arrive at a solution. I think clearly about which tool will best handle any job.
In software development, many people cling to one tool and master it. It can be a great approach to master one tool; it will pay off big for some developers. I prefer to learn many tools and learn them enough to be productive.
follow the 80-20 rule when learning new tools so I can quickly get up to speed with its use cases.
It is of paramount importance to limit the use cases of software. Command-line utilities are easy to restrict use cases with while user interfaces are not. I do not attempt to handle every use case to avoid introducing a diverse collection of bugs. If a solution is fulfilling too many complex use cases I consider creating a new solution.
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 help determine if I have discovered a valid use case.
If I am creating general-purpose software, I limit the use cases as much as possible to general-purpose features that can be extended.
When building software, my philosophy is to version most things. I keep versions pinned, so upgrades don’t happen without a developer’s knowledge and seek a balance between keeping software up to date for security reasons and letting sleeping dogs lie. I abide by the rule; if it’s not broke, don’t fix it.
When naming variables, I name them in ways that make it easy to rename them. I call things exactly what they are and avoid overloading or overusing the same variable name.
I make heavy use of tracer bullets to punch a hole in the walls between myself and a solution. I’ll pick a requirement that exists far away from the end solution and meet it head-on. 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.
There Are No Big Problems
As Henry Ford once said, there are just a lot of small problems. Breaking down what looks like big problems into small manageable problems makes software developers look like wizards. 🧙♂️
If I am facing a complex problem, I find the smallest 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 simply change course on my own.
I keep software components isolated with a single responsibility and create user interfaces that have a single responsibility to reduce complexity. I keep tools divided from one another in a way that is logical for users and other software developers, and I follow SOLID principles when creating object-oriented software solutions.
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.
I work diligently to understand what people need to do with the software I build. 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.
When dealing with difficult problems, I step slowly through the problem. I’ll make a single change then step through a debugger to see the results. Slowly stepping through problems helps me isolate them and solve them one at a time.
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 ever change. An automated test suite helps me sleep at night, knowing that the products I build are top-notch. 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.
When tight couplings exist for no good reason, I will decouple them. I summarily dismiss software libraries that aren’t dependency injection ready. I refactor tightly coupled code to reduce complexity and prepare it for change.
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 know that solutions can’t be fast, cheap, and good at the same time yet strive for the center of all three.
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 own philosophy
Here are a few books that will help you develop your own software development philosophy.