Signs of an Immature Software Developer
pragmaticsoftwareengineering.com
pragmaticsoftwareengineering.com
Only reject a "best practice" if you understand why it was there in the first place.
Maybe I'm just being immature, though!
Otherwise some good points in the article. I'd say a good sign of maturity is that you understand you job isn't to write code, your job is solving problems for the business.
Presumably everyone thinks their own practices are the best, otherwise they wouldn’t be doing them.
‘Accept best practices’ just sounds like ‘do it my way because it’s the best’.
It’s hard to argue with “you should use a version control” unless you have a very good reason not to.
However, if you get into the “you should always use single quotes” argument, in my opinion, that’s too shallow to be a “best practice”.
On a similar vein... whatever it is you're doing, it's an anti pattern.
'There is a Glittering Generality if I ever heard one'
It’s just easier to say “Always follow” as you should always follow best practices until you have a great reason not to.
The identity begins to attach to things that fit that timeline, as in, ‘I am roughly here and therefore based on how I see myself, I should be aligned to what I perceive my identity to be’. This realignment means they eat up any and all bullshit they see at a developer conference or resort to appealing to authority (‘this is how Google does it’). And why wouldn’t they, they are in their eyes almost there, almost like the best.
Ultimately, this is still sincere, just leads to dumb results at times (eh, more like often). It is a form of establishing your own authority by associating yourself with authority, a coopting of something undeserved, but emotionally fulfilling. You feel like 6-7 years is enough, but it just ain’t. In it’s worst form, this is what it is and it’s pretty annoying to deal with.
/end Saturday morning psycho babble
At the start I followed design patterns religiously. I was proud that I'd use those patterns. I wrote about them. I told employers. They employers were impressed. The code was an over-engineered mess. But it ticked the best practices box.
It still happens now. But this is mainly because management and other developers are enthusiastic about various techniques that have the label "best practice".
I last left a codebase in an international firm where everything was a dependency and everything was injected and everything was orchestrated. Everyone was enthusiastic that they finally had a codebase with dependency injection everywhere. I hope to never see that codebase again.
The problem is not so much the individual rules, but the "black and white thinking": believing that everything in programming (or life) can be distilled to very simple rules, without nuance or prior thinking before applying them.
It's a very comfortable position to be in, but it severely hampers your professional development.
Coming to terms with the business side is the first step.
Having your best practises or framework mocked by the next generation of zealots is part of the process. Patterns you know and love become anti-patterns only to return in a distance future in some limited formed with great fanfaire.
Letting go and a realizing your project / patterns will get hijacked by the business side, other developers and often enough pms is freeing.
The solution is to ignore these "my thing is objectively better than yours in 100% of cases" debates - choose the pattern/design/paradigm/thing with the least compromises and do something productive :-)
Usually when people are focused on working and working together fast, the converge on pragmatic efficient solutions.
Of course 'together' is subtle here ..
For the juniors on my team, the next question I always ask after the answer "it's best practice" is always "why is it best practice?"
It's fundamentally important to understand why something is best practice, so that you know when it is not the best course of action.
The reason? It’s a small, rapidly-built prototype hooking two poorly documented systems together while the company makes daily adjustments to the manual process I’m automating.
Everything is in flux because we have just started working with a new vendor that will significantly improve our logistics. All of the chaos is deliberately considered and discussed weekly. (I’m in all of those discussions.)
It’s really beautiful to see this all come together. Once we’ve figured out how this will work, it’s time to firm up the internals and nail it down with tests.
Even if that day never comes, the application has extensive logging (~75% of the application code is devoted to highly detailed logs) and production errors will quickly be detected, with enough information to manually fix a bad import or export.
Once edge cases do come up, you can bet I’ll write tests to prevent regressions.
Best practice is to refer to this as "Chesterton's Fence."
It's actually the more senior devs who go beyond and investigate how things could be done better.
I agree that certain standardisation of practices need to exist. I would rather work with someone who calls even imperfect practices as "standard practices" and had an initiative on the side to explore better options vs working with someone who insisted on "best practices" because of their insecurity and shaky authority.
Zealous adherence to "best practices" is actually a good sign of immature developers.
I'd agree with and extend your remarks, if you can clearly document why you're creating your own best practice, then its OK. Or rephrased in a negative sense, If you can't clearly write and permanently document an explanation, then you probably don't have a good reason.
Corollary: any expert in a particular area should know better than best practices to deal with a given problem.
It's an important stage. But people who stay in this stage too long become intolerable technical architects who yammer away about best practices and high level company goals, produce technical documents no one reads and plan team building events no one wants to attend.
To me stage 3 involves thinking critically about tradeoffs. Applying best practices in relevant contexts and rejecting them in others is a good example.
I always end up linking this silly blog post. https://medium.com/@webseanhickey/the-evolution-of-a-softwar... It sums up the seniority growth cycle better than I can in words.
Best practises are negotiable if you can attack them with first principles. This is the basis of critical thinking, which I think is more important than best practises.
Best practises will get you fast social agreement though, whilst critical thinking will upset many people because you challenge their idea's trying to find issues early or better options.
This here is a sign of immaturity.
Following Best Practices to the letter without knowing why is fine if you're a student or a junior developer, or if your work is not much off the beaten path.
But as soon as you're doing anything more complicated than stringing libraries together you have to know what you're doing and why you're doing it. You have to know the tradeoffs and benefits. This means discussing and yes, negotiating what are considered "best practices", and sometimes rejecting them if they're getting in the way of having a good product.
But I fully agree with the rest of your comment, the more decades I spend programming, the more I realize that the answer to pretty much any question related to SW development should start with "It depends. ..."
Yep. I was thinking of the same thing when writing other comments. It's very comfortable to "believe" one has the perfect answer to an entire class of problems, but it's never the reality.
If you reject everything because “you know batter” that’s a totally different story.
A lot of this is common sense stuff, but it might be better to replace this part with "Write tests." TDD is the kind of thing that sounds great in a blog post, but doesn't actually seem to work in the real world. At the very least, TDD is not a benchmark of a "mature software developer."
The one place where I think it works well is implementing a well defined protocol. If you have exact definitions for inputs and outputs, then writing the tests first can make things a lot easier. But it's rare when you have such a clean problem to solve.
As an example, any data infrastructure software that uses non-trivial adapative scheduling to optimize concurrency and throughput famously has this property. It is also the primary reason things like database engines are effectively untestable until the code is nearly complete. In principle you could architect any software to be amenable to TDD, but for these cases the performance penalty implied by that architecture is so severe (literally on the order of 10x) that no one serious would design their software that way.
In these cases, the same binary can pass testing on one machine and fail on another. It is a different discipline than classic TDD, and more critical as more software systems are implicitly distributed.
I’ll admit that I also lack (and have lacked) guidance on testing.
All I can give is my anecdotal evidence. Most of our development teams apply TDD most of the time. We're working on a mature (5y) enough codebase to have a stable foundation. We do lots of integration testing and our testing is not without issues (it's slow, some of it is flaky, lots of test fixtures that are a pain to manage) but those issues do not come from TDD and are being dealt with piecemeal.
The only downside of TDD I've personally witnessed in practice is tendency to write superfluous tests. They either don't test anything useful (e.g. things already guaranteed by types) or leak implementation details. But we encourage developers to improve those tests and the engineering team is overwhelmingly positive that TDD has been a net win for productivity.
We try to deploy as much pair-programming as we can and the TDD methodology is specially improved by this. It helps dealing with loose ends from the task description or the technical kick-off and double-checking if the task acceptance criterion makes sense. If one developer of the pair has to take off halfway through, the most important bit is already agreed upon and the code review is much easier later on. Or if the pair-programming starts halfway through, the arriving developer can read the tests to catch up and help make the other tests pass.
People are all getting mad about the "best practices" bit as if it's not just a fact. Does it hurt that much to admit that maybe you're not always the mature developer you'd want to be? I sure am not, I always get carried away writing cowboy code, maybe writing tests after, maybe not because I'm in a hurry and we can stand a bug or 2, and the CI/CD will catch any syntax errors anyway.
The rules are simple though. You determine the best practices, you apply them, and your code will be more correct, it will be written faster, you will spend less time being confused, your final product will be closer to the specifications, and it will have less bugs.
How could this be contentious? No way there's anyone out there that truly feels they wouldn't push less bugs to production if they always could and would apply TDD.
TDD is PITA and I do not know any one soul that actually follows it. But it does sound good.
Best Practices are a dozen a dime and if anything they can serve as to identify which kind of books the team lead once read.
I could go on but I'm seriously bored by this type of posts/attitude. I only commend because nowdays they seem to come disguised as "pragmatic".
There are some valid points in there but there are zealot red-flags as well. Funny enough 'no zealots' is one of the points.
Preach it!
I am dealing with this now, having taken over a project from someone who thought this way. Yes it works, but is full of bad error handling, cryptic exceptions, race conditions (particularly TOCTOU bugs), non-atomic database updates, and more.
"But it works" they say, which is true. But a simple SQL query shows duplicate or missing data where there shouldn't be and other violations of basic assumptions.
Looking at code and knowing what could possibly go wrong is an extremely useful skill. Unfortunately it usually takes running head first into those issues to actually learn and comprehend those lessons (which I have done more than my fair share of :))
It feels as though the author has taken an authoritive, slightly condescending stance towards his readers. The message it sends is that the author is out to invalidate his readers, judge them, not take their hand and teach them. I assume the intention was otherwise, but it is not presented as such.
It reeks of a junior with strong opinions rather than an old software jedi that intends to teach us the mastery of the art.
It’s really the discussion here on HN that’s curing it, which is fine. Someone had to get us the meat before we could cook it. There’s a lot of consensus here and good counter points that aren’t all damning (half it can be chalked up to inexperience, or effort vs value debates, eg TDD, impressionable developers, etc).
Even to call the signs 'immature' isn't helpful, as it implies that a dev is not as mature as they 'should' be. An illustrative quote is "Immaturity is something that comes with age." Junior would be a more fitting description, as that's only referring to their current progress rather than a judgement.
In my experience (over 20 years) rules are meant to be broken. You can’t have a set of meaningful rules that can’t be misfits in some cases.
“Write unit-tests!”
- on a shell script?
- for my playbook?
- on that static html-css?
- on my docker config?...
You’ll learn that best practices are great guidelines when you begin. Once you’re seasoned you’ll know what’s right... that’s what make you senior. You don’t ignore them, you use them wisely.
As a founder, I have found that the tech stack choice does matter beyond just getting the problems solved.
When hiring, engineers have judged joining my company based on our tech stack. Not just about familiarity, some have outright said that it’s because they only want to learn the most popular and transferable skills.
When discussing strategic acquisitions, it also matters. Interested buyers (ie for an acquisition) have asked about our tools and languages. We are more valuable if our languages and tools are compatible / common enough to fit into their organization.
If your “mature engineers” are business- and team-focused (read: doing what they are told), disregard innovations in tech and evolution (and evangelism) of best practices, you’re gonna have a boring team that will eventually get disrupted either from outside or inside of your org.
"Doing what they are told" strikes me as the antithesis of being business and team focused - particularly the former.
A non-business focused engineer is mindlessly implementing the solutions the business asks for, without taking the time to understand the problem, motivation or mental-model of the business.
A non-team focused engineer is narrowly implementing the tasks that end up assigned to them, with no interest in what the rest of the team is doing, and not engaging with the wider team to challenge and contribute to what they are doing.
I may be wrong tho.
Edit: here's a real critique.
> Best practices are non-negotiable
Have you ever argued about which practice was best? I have, many times. They are endlessly negotiable, and there is no canonical list of best practices.
What it means is whatever you or your company decides are best practices as well as agreed upon industry best practices must be followed every time.
This is only good advice if you're solving very simple, very trivial problems, or if you don't have enough experience. (That, or if you redefine best practices to tautologies like "always test" or "always write good code").
For anything more complicated than the most trivial of software, it's important to deeply understand your best practices and if they make sense to each situation or not.
Software is hard. There are no silver bullets.
But maybe CI is slow and flaky, and a business test fails on a PR where you've only changed the README.
Sure, you can say "well, the rule is to only merge with green CI" and restart the failed job, but you can also think "well, I can merge this".
Maybe it's ok, maybe not, but I think you should trust your fellow developers with doing the right thing rather than berate them "you didn't follow the rules!"
Only a sith deals in absolutes.
The real trick is a) getting your team to agree on what practices to follow, and b) agreeing that if you’re going to deviate from them that there’s an expectation that you’ll need to justify that.
“I did it this way because it’s the first thing I thought of” vs “I reasoned my way through this and this is why we should do it this way” are very different situations, even if they result in the same code.
On the contrary, I think that a sign of a mature software developer is understanding why particular "best practices" exist, and when it's appropriate to reject them.
For example most modern languages have list comprehension and some kind of built in map-reduce on a collection built in for free, nicely debugged, etc. Once in awhile you'll run into noob code where someone writes their own long elaborate and buggy routine full of if/then and goto to slowly iterate a pointer thru an array, sometimes skipping the first or last element of the array, etc. But if all an immature dev knows is "if then and goto" and not that fancy list comprehension stuff, you can get some crazy looking code that can be optimized, sometimes, down to one idiomatic line of code.
There are of course other examples of non-idiomatic code. Now is DRY don't repeat yourself idiomatic or a best practice or both or ?
On the positive side, fixing bugs in his code was often easy. First I refactored hundreds of lines of code into ten lines, then I added the missing "if".
As it is now, your post looks like chapter list in a generic "Learn SW dev in 24 hours" book.
Staffing and code maintenance are business problems. The tech matters at various levels of concern.
Staffing and code maintenance are a business problem but I see software developers early in their journey put too much weight on these compared to bottom line value to the company.
I have seen inexperienced developers and managers -and also developers with a lot of wrong experience- sink enormous amounts of time/money into maintaining extremely bad code without even noticing where the cost came from.
Of course stakeholders often have a way of upending your beautiful design, but one of the core tenets of agile is to design your code to be adaptable. Done upfront, the cost is usually minimal but can completely save you when your client asks for the dreaded "small change".
YMMV of course. It's a big world out there.
"Business problems" aren't constrained here to "startups". Most business concerns are things that already exist in successful companies, because they are in business.
In such cases the IT department is most likely a couple of managers for external contractors delivering projects organized per budget packages.