The fender on the Apollo 17 rover fell off. This was a big problem – dust on the moon is very damaging to delicate instruments and suits.
Luckily, there was a solution – literal duct tape. NASA advised the astronauts to put together some maps in the shape of a fender, and attach it to the rover with duct tape. This fixed their problem!
Of course, this solution is not ideal – the original fender would have worked much better than this. However, with the resources available, this was a good decision – the makeshift fender worked.
Similar questions can come up when making software:
"Hey, is it ok if I just skip writing tests for this one?"
"This is a bit of a hack, do you think it's ok?"
"This isn't the way everything else is done, but it's easier. I think it's ok?"
These are common questions, questions that I have asked many times, and have been asked many times.
How can you tell when a hack is too much of a hack? How do you know if you're being principled, or pedantic?
Decisions like this fall on a spectrum. I think of this spectrum as the "Idealism and pragmatism" spectrum.
Pragmatism is "dealing with things sensibly and realistically in a way that is based on practical rather than theoretical considerations."
In the context of software, this surfaces as a way of thinking about utility. A developer who concentrates more on outcomes (delivery, shipping, releases, or features) more than how those outcomes are achieved, is a pragmatic developer.
In practice, this can often mean a willingness to do "hacks". If there is a non-ideal path to a good outcome, taking that path might be pragmatic.
As an example, in the browser, maybe you need to override a
window method to fix an urgent bug. Or maybe you need to disable a test to make builds pass. Both of these things are not ideal, but they arrive at a good outcome.
On the other end of the spectrum is idealism. Idealism is
the cherishing or pursuit of high or noble principles, purposes and goals"
In software, idealism surfaces when we have a strong principle that we do not violate. A developer who focuses on what is "right", or "the best way to do things" may be idealistic in their attitude.
Idealism in software development practices can come down to blocking things, or saying "no" to things being done incorrectly. It might be deciding to not do a hack, or by spending a little bit more effort to do something the way that it "should be done".
Apart from just saying no to wrong things, idealistic practices might mean writing down principles, like "Document every new feature", or it might mean creating linting rules that force people to write code in a consistent way. The exact principles will depend on the project, but idealism is always about aiming for some high standard, even if it's hard to get there.
These idealistic approaches trade off some short term speed for a long term benefit, like general code quality and consistency, test coverage, or complete documentation of features.
Often decisions need to be made, where the answer falls into one of these two categories. Should you be pragmatic, or idealistic?
You can't be idealistic without having some principles written down, but you also cannot be properly pragmatic without knowing your principles. What is it that is valuable to your project? Having these principles clearly written down and being shared knowledge on your team will help you assess how badly you are violating one of these principles, if you need to.
Maybe you have a principle of "The user experience comes first", and you need to make a choice – delay delivery for one week, or deliver a somewhat buggy user experience. Having that principle written helps you acknowledge what is gained and lost on each side, and make the best call in that situation, acknowledging the costs and benefits.
Pressure is normal enough when developing software – there are deadlines, roadmaps, and releases. Often, a more pragmatic choice is appealing when one of these pressures are present.
Try and break down exactly what the pressure is. If there a large impact for a certain outcome (other teams / users are depending on a certain deliverable), that is a large impact, but maybe the deadline was just a date selected as a likely shipping date, and the impact of delaying delivery is low.
Knowing where you sit on the spectrum of idealistic to pragmatic is a good first step. Do you often find yourself being "lazy", or taking shortcuts? Or do you find yourself too often being "stubborn", and frequently saying no? If you aren't sure, you can ask people you work with, who may have a good idea of where your natural tendency lies.
Personally, my tendency is to make pragmatic choices, and I need to put in conscious effort to ensure principles are created and adhered too. (Tools and rules help a lot in this regard, as they are rigid standards that I can't forget about).
If you know how you're naturally biased when making decisions, you can step back when you sense yourself making an overly pragmatic, or overly idealistic decision.
You see something that is not ideal in a pull request. Maybe a test was forgotten, or some could should be refactored. Should you withhold approval?
Things to keep in mind here are that you are setting a standard in your pull requests, and an idealistic approach here is valuable for future work (the standard you walk past is the standard you accept)
However, there might be pressures (see: know the pressures above) that necessitate skipping some of these things.
A pragmatic compromise might be, "Ok, let's merge and release this, but follow up with the proper fixes".
The important thing to monitor here is that those fixes actually occur.
There's a way to do something that is clearly not ideal – you're breaking an API, or you're doing something that is so obviously wrong, you're almost embarrassed. However, it gets the job done.
Is this ok?
You can examine your principles to see how much it violates them. Is it a worse UX, worse code quality, or a risky change? If it heavily violates your principles, best to not implement this hack.
On the other side, there are times when hacks are useful. Maybe it speeds up other developers, in a way where their code dodes not need to rely on this hack. It may allow you to deliver a high impact release on time, that users or other developers are depending on.
In your decision, you might include a plan to remove this hack in the future. In this plan, make sure you include not only when you would fix this hack, but also why you would fix it. Why might be a ticket, or process that would ensure this gets done. Hacks often live a very long time, so you should be sure you're ok with it being around for a long time.
Outside pressure leads towards the pragmatic approach – outcomes are what's important, but the way those outcomes are achieved is the domain of the engineer. This means that, left unchecked, bad habits and practices can creep in.
In this case, it's important to realise that this is happening (see: Actually have principles), and combat it. This can be by explicitly calling out the principle, and through pull requests, encouraging your team to adhere to them.
Sometimes, first code of a new type is being checked in. If you are doing something for the first time, be aware that you are setting a precedent for all similar, future work. Are you making the first GraphQL resolver, the first API model, or the first shared library? You are setting a precedent that many developers will follow! In this case, an idealistic approach is always preferable.
There is a spectrum of pragmatism and idealism, but these approaches are not diametrically opposed. Most of the time, we can have both an ideal outcome, and ideal path. Often the best way to deliver high utility, even in the short term, is by doing things in an ideal way. This is not always the case for some harder decisions though, and there are times when tradeoffs need to be made.
Do you fall more on the side of pragmatic, or idealistic? Do you have any tips on how to know where your decision should fall on this spectrum? I'd love to hear!