Better Code = Better Results
A developer-focused dive into custom application development.
Building a program or web app to solve a problem or provide a service is an incredibly popular thing to do - it allows a business to run more smoothly, using something that’s custom tailored for them to be smooth, simple, and unified. But the path there can be fraught with pitfalls. This post will go over some of those pitfalls and talk about what to focus on to delight the customer. This post is mainly technical - speaking to developers and engineers on what their technical priorities should be when building an application. For a more overall process/philosophical overview see here.
There’s lot’s of technical choices to make when choosing to develop an application. Languages, frameworks, cloud service provider, and more. But I’ve found most developers are actually pretty good at choosing those. There’s a couple of reasons for this. First, the most used and most popular choices are often used and popular for a reason; and second, these tool choices generally grant only marginal advantages and disadvantages relative to each other, and are often a matter primarily of preference and familiarity. But there are some choices that really do matter. Some are obvious and some you might not even think about. Let’s dive in.
Start with your data model, and build your application around it, not the other way around.
Data is the core of any line of business application, web or not. And interacting with that data in a straightforward and performant fashion is your app’s raison d'être. Spending some time to think about what your data should look like when it’s stored before you start building your UI unifies all your usage. Two different places in your UI might look at a piece of data differently. Say for example you need to store a person and their attendant information. And the UI you are building wants to display that person’s address. You might thus be tempted to store this address on the person record. But some other location in your ui that you aren’t working on yet might need to allow for multiple addresses. And maybe another one doesn’t care about addresses at all. If you develop your data store as being a consequence of your ui you can end up with a mess, having to migrate large amounts of data out to related tables, or even a different storage solution. That’s not only time consuming and obnoxious, it’s also high risk and difficult to test. Thinking about your data model first should help you think about your problems holistically, instead of designing it for one sub-case of your overall usage model.
Keep things simple until something actively forces you to make them novel.
As developers, we love to write cool code. It feels good, it makes us feel smart. We read about patterns and strategies and algorithms, and we naturally want to try them out where we can. Resist. that. urge. Focus on understandability, simplicity, and readability. Your code should be natural and immediate to grasp for a green, entry level engineer, or an engineer brand new to your code base, 99% of the time. Sometimes circumstances force our hand, and we have to do something complex or unwieldy, but make circumstances force your hand.
There are reasons for this unyielding focus I have on simplicity. First, general wisdom holds that somewhere between 60-80% of development time spent on applications is spent in support development, not greenfield work. Anecdotally, I have found that to be accurate in my experience, and it makes sense - applications that are actively being used tend to need changes, bug fixes and enhancements. It’s a side effect of making something people actually want. So if you want to optimize for that 60-80% of the time (and you should), write intentionally simple code that follows existing patterns wherever possible, to increase your time to understanding later.
Second, unless you’re a one dev shop, or even if you are, you are unlikely to be the only person working on your codebase over the lifetime of an application. And regrettably, I have found that most people are unable to read my mind. They don’t immediately remember that I used a strategy pattern in situation x for no particular reason instead of using a switch or if statement. Heck, even me a year after I wrote it probably doesn’t immediately remember that, and I know that me a year later is going to be mad at myself when I do remember.
Third, simple code produces less and more readily understandable defects. This is intuitively true. Code that has good “at a glance” value is inherently harder to screw up, and it is generally easier to track down defects in because the tracking process is simpler and less onerous. This is a simple point, but hard to overstate.
Test, test, test.
I’ll be honest with you, I hate testing my code. I cordially dislike unit testing, and I really hate actually doing manual user testing of my code. But I do it, and I demand it of people on my teams wherever possible. The benefits of unit testing, whether after the fact or as TDD are written about in a million places, and I’m not going to bother to enumerate them here. I’ll simply say that while it isn’t a magic bullet, and some of the zealots of it overplay their hand, writing unit tests is immensely positive and you should do it.
What I DO want to spend time talking about is manual testing, and most of what I write here also applies tenfold to automated testing that simulates manual testing. It doesn’t matter if I have to spend hours figuring out how to manually test something I wrote, I’ll do it. It’s immensely beneficial. For one, if a developer doesn’t know how to manually test their work the same way a customer would use it, then they don’t understand their work. And that just doesn’t fly. If a developer doesn’t know the implications of their code from the customer’s point of view, then whether their code provides the intended customer value is a total flip of a coin. So, even if the impact is somewhat opaque: let’s say, a cron script that conditionally updates some records that impact a few different places, I’ll take the time to walk through each case, exactly how a customer would use the product. It helps a developer understand the product as it’s actually used, not just as an abstract coding challenge, and it helps the developer understand how all the myriad pieces of an application fit together to form a hopefully cohesive whole.
Conclusion
Let’s sum this up. My basic thesis here is that if you follow the above, it will help you design and develop systems that are more reliable, less buggy, and easier to modify and maintain. I would submit here that that is what a customer really wants. I’ve worked on good code bases and bad ones throughout my career, and lord knows many of my lessons I’ve learned through my own blood, sweat, and tears. But this I feel quite strongly on this - at the end of the day, customers want something it works and is resilient, and these principles. when followed help with that. Good luck, and happy coding.