In the last few years, you’ve probably heard these phrases a lot.

Can we focus on the problem in front of us?

We need to be practical here

Let’s try to be pragmatic

We’ve all worked with engineers who care more about applying SOLID principles than delivering working software. As an industry we’ve been reacting to that – and rightly so. But I have also seen this language used to shut down conversations that – though they may have seemed insufficiently pragmatic at the time – turned out to be very important. We push aside conceptual problems to focus on practical problems, but today’s practical problems are often symptoms of the conceptual problems we pushed aside yesterday. Pragmatism can become a shortsighted cycle of conceptual neglect.

Conceptual Problems You Should Care About

Some conceptual problems can be kept in our peripheral vision until we understand them better, but others need to be addressed right away. How do we know which is which? To try and help, here are some of conceptual problems that have caused issues for my team. When we see one of these now, we tackle it right away.

Inconsistent Thinking

If your code reflects different ways of thinking about and solving the same problem, clashing mental models will cause confusion sooner rather than later.

In publishing certain messages for downstream systems to consume, we needed a mechanism to identify which billing period each message related to. Different messages were implemented by different people over a period of several months and when I examined the whole subsystem at the end, it was clear that the logic for finding the billing period had been invented anew by each engineer. Depending on the message, it was sometimes being found by the bill, sometimes by a created-at timestamp and sometimes by a combination of the two. There were 3 different strategies implemented across 5 different code paths.

When I expressed concern, I was encouraged to be pragmatic – after all, the integration was working fine. But we have enough users that errors in thinking quickly manifest as mishandled edge-cases, so I wanted to get out in front of this one. I began converging our various code paths and strategies around the most sensible solution.

Just before I completed the job a Product Manager in another team sounded the alarm – their numbers weren’t adding up because certain messages couldn’t be placed in billing periods and it was costing us money. Because we had begun solving the conceptual problem instead of waiting for a practical problem, we were able to resolve the issue on the same day and avoid serious financial harm for the business.

Self-Defeating Compromises

We make trade-offs to get things to market faster, but there’s no point delivering something faster if the core value remains unrealised.

One of our teams developed a service for centralising ownership of certain account information. Downstream systems had been designed to expect this information with a special key, but so far only one key had been used. Developers of the new upstream system decided to save time by only supporting that one key.

But downstream systems weren’t designed to expect a key arbitrarily – being able to differentiate these records by key was essential to providing a good user experience. As soon as the business added a second key, the upstream system was rendered useless. The compromise was self-defeating, in that it undermined the core value of the work.

Misleading Behaviour

Anything that doesn’t behave the way it leads people to think it will behave is an expensive mistake waiting to happen.

A member of our team named the primary key column for a new fees table transaction_id. As the team already had a ledger table called transactions and a convention of storing off-ledger supplementary data in other tables (with transaction_id as the foreign key), the naming decision misled many engineers into thinking that the new fees table was another example of this pattern. Much time was wasted explaining to people why they were not able to find those transaction_id values in the transactions table. When the misunderstanding began to influence people’s design decisions, we stepped in and changed it.

Lost Information

We all know not to throw out important information. But many engineers conflate what happened with their interpretation of what happened, discarding the former and only storing the latter. I wrote a whole post on this topic called Write Now, Think Later.