10 Things someone will pay for later
This is based on a talk I did at the 2015 Orlando Code Camp, but it strikes me as something worth blogging about. This is not meant to be an exhaustive list of anti-patterns, but just a general category of things that end up being a huge pain at some point. Avoid these at all costs! In no particular order...
1. Poor or excessive project organization
We've all been here, right? There are people who show up to the party and believe, "We're doing agile, there is no organization!" Of course, you know better, that Agile (big "A") is anything but free of process. Still others build kingdoms, throw up walls and generally get in the way of collaboration. While teams certainly make things happen, you still need a product owner who ultimately can drive feature decisions and understand what the stakeholders need.
I still think it's the excess that gets in the way the most, however. On one hand, there are people who want to specify every little detail of an application before even a single line of code is written. You know how this goes... the outcome is never what they thought they wanted. At the other end, you have people who believe we need 40 people to sign-off on something to get it into production. And no, SOx regulations don't require that, so don't be suckered into that line of thinking.
2. Naming stuff with names that no one but you understand
Stop using Hungrian notation. Your fancy IDE is pretty good at doing mouse-overs and discovering types. Use descriptive names for stuff that make it totally obvious what you're using. Conventions are also good, so if you have some group of things that generate stuff, use the word "generator" in the names (like "UnicornGenerator"). Be specific, too. It's much better to have something called "BostonTerrierGroomer" than "DogGroomer."
3. Dependencies on hard system boundaries
I would agrue that this makes me rage more than most things. If the smaller parts of your software can't be tested or worked on without a database, file system or other external service, you're doing it wrong. Dependency injection should in the general sense be pretty familiar to most people by now, and I ask you, please, to embrace it and understand how to use it. Part of the reason it's critical is that you don't want to create barriers that make it difficult for a new developer to ramp up and start working on stuff.
Of course the system needs those dependencies when it's running for real, but sometimes they do break. How do you deal with that? Instrumentation! Believe it or not, it's something that even big enterprises don't do well, and you can seriously consider this a career opportunity for hero status. It sounds obvious, but when you can dashboard the connectivity, latency, queue lengths, etc., between system pieces, it's easier to diagnose problems. Even better, start passing around context between systems and make them record it. Not sure which of the dozen systems that acts on your data did something naughty to it? Leave a paper trail!
4. Mixing real-time and pre-calculated business rules to output data
This is not entirely obvious. Imagine if you will, an app that dispays your checking account balance. If it's truly a ledger, then the balance is the thing on the last line. That's pre-calculated data. Of course, you could also find the same number by taking the starting balance and then applying every adjustment to it. Where you get into trouble is a system that does both under certain circumstances. All of a sudden, a change in code alters the downstream outcome of the value you're expecting. I've seen this happen so many times because there was no deliberate choice to either calculate a value in real-time, or calculate it ahead of time. It's super important.
5. Testing all of the wrong things
This is another one of those topics that turns into religion for some people. Testing a method that is simply calling one method from a dependecy seems like a poor use of time. I also see a lot of people getting tests involved with the functionality of frameworks. I'm pretty sure that MVC has good code coverage, so don't test it! If you find yourself doing really complex setup for unit tests, you might have a design problem (ORM's and other leaky abstractions are notorious for causing this).
Unit testing each logical path through code is usually pretty adequate. Integration tests are great if they make sense and add value by validating how things work together. Functional testing is awesome as well, if you can afford it. Above all, automate testing and make it part of the build.
6. Putting off refactoring on something that is clearly broken
If I had a dollar for every time I've seen this, I would be sipping fruity drinks on my yacht instead of writing this blog post. Anyone who doesn't believe that technical debt is real hasn't seriously encountered anything on this list. Using the debt analogy, interest accrues over time. In our world, interest is actually fading contextual knowledge. The people who know what should be going on either forget or leave. Stakeholders need to understand that performance is in fact a feature, that there's a cost associated with it. Figuring that out later is not so great for users.
Keep in mind that I'm not advocating premature optimization here. That's not a good use of time and money at all. That said, it is important not to be haunted by poor decisions early on.
7. Libraries, packages and poor source control
I'm shocked at how many small organizations don't use source control. They keep code on a file share somewhere, and have no verion history, no way to roll back. It's a bad scene. Distributed source control in particular is awesome, because it allows developers to branch and merge independent of the team at large. These days, it's a good idea to break out common libraries used across an organziation into a NuGet repository, strongly typed and well versioned. Avoid making hard "library" references to packages included with your source. These are a lot harder to upgrade.
8. The guy with all of the context was hit by a bus
If you rely on a subject matter expert (SME), make sure that they're not the only one on the planet. Right-size your documentation, if you have any at all. Remember that good test coverage makes for clearer context (assuming you name your tests well). Don't let a single developer hold the entire business hostage.
This lives in that zone where people problems are in fact harder to solve than technical problems. I often make the point to people that in terms of career advancement, you can get a long way by demonstrating the ability to resolve the people problems.
9. Classes that are a thousand lines long
Keep in mind that I'm talking actual code, not comments and documentation tags. I don't like to generalize, but usually long classes are doing entirely too much. It tends to violate ACID, KISS and other acronyms. You really have to emphasize that code should be as readable for humans as it is the machine.
More to the point, all of that code is likely harder to unit test. When logic goes several layers deep, it's hard to understand what it does. Worse yet, even if you are the original developer, you may not remember what the context and purpose of the code is. If code defines your business rules, and you can't decipher it, then what?
10. Hard to replicate environments
My general rule is that if it takes longer than an hour to clone the source code and get working, your environment is probably too complicated. If you have an app that has a bunch of external dependencies (and you know what I'm talking about if you consult to giant enterprises), you need to stub that stuff out and make it a part of your local solution.
"Hard" can also be described by way of your tooling choices. If you decide you want to use some hot document database, there's a cost associated with that if others don't have the skills to work with it. That's true of all kinds of tools and frameworks. Don't let shiny objects make things harder for people to work with. It's not a win.
And finally, automate things like configuration and seed data. Then make sure the automation is part of your source control so there is no mystery about how to use it.