There is an observation that the easier is the problem some piece of software intended to solve — the more elaborate its architecture will be. I’ve been working in multiple fields from academics to web-development, and I can confirm that it’s universally true. On my current job, I work with rather sophisticated algorithms, and we are quite happy with the one and only pattern for everything. But on my former job, I’ve had to deal with a monstrous 14K SLOC so called Data Provider which was designed solely to move a piece of data from one region of memory to another. Needless to say, it was 99.9% architecture and 0.1% memcpy
.
It’s like there is some sort of “comfortable level of complexity” and people feel uneasy building things simpler than that.
If your problem is essentially complex, like 3D boolean operations in floating point numbers, then you spend all your cognitive capacity on avoiding numerical issues and algorithm inconsistencies. You don’t want things to get more complex than they are. But if you’re doing something as trivial as reading a parameter from a configuration file, you can’t just read a file and find your datum; you need lexer, parser, adapter for a lexer to make it work with the parser, a handler to read the value, and a delegate to push this handler to the parser. Oh, and you need a factory for your handlers. And a parser should also be a singleton.
Well, it shouldn’t. Not that a parser shouldn’t be a singleton, most of the time you don’t need a parser at all. The whole solution shouldn’t be all that complex, reading simple data is essentially simple.
I don’t know how come it often grows like this, but I have a theory. I meant to write “always” here, but I saw a few lucky exceptions, so I wrote “often.” The thing is, these exceptions have something in common. They were all written by extremely self-confident programmers.
My theory is, the main source of accidental complexity in programming is insecurity. People just don’t feel confident enough to write simple code.
When you’ve grown into patterns and practices, you don’t really have to take any responsibility for the result. No one ever got fired for following GoF. It’s only doing really simple things that is risky.
What if I read UserName
from a file, but then the scheme changes and it’s suddenly NameOfUser
? What will go wrong then? Well, if I make it configurable, then this is not my responsibility anymore. I’ll just write some code for that, and add a couple dependencies, and think out a few data structures no one would ever care about. Anyway, isn’t is a good practice?
The code is much easier to write than to unwrite back. So yes, while it is generally a good practice to make your solution configurable, it is not necessary the best solution for every occasion. What if the scheme will remain untouched for the next ten years? Or what if it changes once in 2027 and it will take you to edit one line of code to make it run for the next ten years? I for one would definitely prefer a code that is expandable due to its simplicity to a code that is written to be expandable by adding more code to it. If I get one dollar for every time an architect predicted an expansion ten years ahead exactly like it happens, I would have exactly one dollar (Hi Wout!)
It’s not like there is a box of ready made decisions, nevertheless gurus and evangelists want you to think. Sometimes you have to choose the complexity, sometimes you don’t. The paradox lies in that you can’t write a book on choosing simplicity. You can’t give a conference talk on that. This would be an unsuitably short talk and one page books don’t sell very well either. So when you feel insecure, there is simply no authority to back your decision for making things simple.
Writing simple code takes confidence. I can’t help you with that. But perhaps I can provide reassurance. If you feel like what you’re doing is objectionably simple, just ask me for an independent code review. My email off work is [email protected]. I am reasonably free every other weekend, so let’s give it a try, shall we?