Software development topics I've changed my mind on after 6 years in dev
chriskiehl.com
chriskiehl.com
You don’t know the constraints the previous person was under, so you can’t possibly know what you really would have done or what “should” have been done.
I mention this because there’s still a tone of, “there is approximately one right way to code” in this article, and I really doubt that.
Make good choices now, but don’t be harsh to the folks who came before you.
I work in tech diligence, I talk to founders about how their project got the way it is when they are selling it, and what the potential ramifications of those constraints and the resulting decisions are for the acquirer. Most developers have little to no understanding of the constraints under which businesses are built and are quick to complain that code must have been made by idiots without thinking of all the drivers that might have created a situation where non-optimal code was a good (or the only possible!) decision.
Tech debt absolutely kills companies - I've seen bad tech debt torpedo potential acquisitions or result in very significant changes in valuation - but not as much as does running out of money.
Conway's Law says the structure of code mirrors the structure of the organization that produced it. There's a correlary to that somewhere that says the financial history of an organization is embedded in the structure of the code it writes.
I've also started having problems with the phrase "Technical Debt." Talking with a non-techincal co-founder once about why they had so much technical debt, their concept was "debt is great! we cut corners today and pay it back when we get more funding!" I had to break it to him that unlike regular debt, where it's easier to structure budgets to meet interest payments, technical debt reduces velocity making it harder and harder to reliably deliver software on time and on budget. The interest payment on technical debt is super-polynomial, not linear.
They don't understand that the interest rate for technical debt is worse than any loan shark. At least that's what my estimate is. I thought this was a sufficiently interesting question that I made an Ask HN thread [1]
[1] - https://news.ycombinator.com/item?id=32175184You don't know what you're going to have to do or when, but you do not want to take that obligation lightly.
In the end, how did the non-technical founder take it?
I was taught that computers don't care how you format things, where you place things, what language or principles you use, etc.. All of that is for OTHER developers.
The machines do as instructed, but all the fluff around coding is so other developers can see what you were trying to tell the machine to do.
Since the micro-service craze started, I've been yelling this from the rooftops. Making everything a micro-service by default is pure insanity. The overhead to maintain them requires exponentially more resources than a monolith or a few services. A few weeks before I left my last job, a very high level engineer gave a presentation where he stated, in all seriousness, that we should be producing one micro-service per day with the goal to be two per day after one year. I didn't need any motivation to leave but that would have done the trick.
Good for you that you left though :-)
What are some solid companies with competitive pay that only use microservices when the need arises instead of by default, using monoliths otherwise?
This absolute is somewhat misplaced in my experience. Yes hitting 100% code coverage does not inherently make the code better, but it does force people to look up the tests that were there before and it often helps with providing some form of context.
"Code coverage is only loosely correlated with code quality" would be my take.
The main takeaway is that if you test your code on the wrong ("unit") level, you may easily get ~100% coverage, but that is mostly worthless. Test on higher levels instead, ie. against business requirements, and don't worry about code coverage that much.
We've noticed that you can still have all sorts of problems in code that has 100% coverage. We started using a TLA+ like "environment" to automagically prove assertions about our code (mostly things like "there's only one path that gets you to this counter := counter + 1 line and there's a mutex around it.") It's a pain in the rear, but probably less of a pain in the rear than trying to prove that sort of thing with traditional tests and an exhortation that you have to have 100% coverage.
this is a good one to pick up early. Looking forward to my 20 in dev.
If you have a tradeoff between something today and clarity you might thank yourself later that you didn't went for it and kept code amenable to change. Or as with any tradeoff you can be right going for that cleverness.
I have learned that I want to err on clarity side not on cleverness side but ymmv.
Certainly you can go wrong by chasing high code coverage, yet I have only one application that I have written that has had zero bug reports filed against it. On a whim I decided to try for 100 percent coverage and in the process found a small pile of bugs. Since then nobody else has found any. At least by that metric it is high quality.
It's certainly possible to make high quality code without tests (see Linux), but simply taking the time and writing tests seems to accelerate development.
If you enforce coverage on a busy team with less experienced or less diligent engineers, you're going to get a lot of "tests" that just pass for coverage purposes and gives false confidence in your code and doesn't gain you anything.
It's also the only company that I've worked at where you can commit code and get up from your desk.
- Some bugs aren't triggered often but are still bad when they happen. Sorting in Python and Java/Android was broken for ages, but not in a way you'd hit often.
- In keeping with the above point, failure rates are exponential in the complexity of your software in the presence of buggy components. Future changes and additions will be problematic on a buggy foundation.
- Many bugs go unnoticed while still causing damage. You know that kind of "oh shit" moment when you compare the last year of payments in one database to another and find duplicates or missing records? You're not sure yet what went wrong, but _something_ definitely wrecked a lot of people's days.
- Similarly with anything involving proper subnormal support, bit-accurate transcendentals, .... If you don't have a good idea what the result was supposed to look like and it was built on a numerical house of cards then you might get lucky most of the time, but some of your outputs are going to be subtly wrong in ways that definitely hurt the business and probably won't be picked up on for ages.
And so on. Maybe it's not worth fixing the bugs in some piece of software (balancing constraints and all that), but there's definitely value to be had.
Does anyone really stress over these? Everywhere I’ve used them it’s always been use black/flake8/eslint/prettier with mostly out of the box settings in order to get some low hanging fruit standardisation of the code to avoid people stressing/arguing over them.
If a code style check is worthwhile then it should be PR-able into one of the mainstream automated code linters, style checkers, or formatters.
In one case the person had seniority over me and kept "refactoring" our codebase in a team of 8. You'd write a function that worked one day and come back the next and it was broken because "I changed the name of that function because it did sound right".
Some people are weirdos and nothing short of their manager telling them to STFU and do their job will stop them.
// I prefer to look at this:
function example( whatever ) {
:
}
// Instead of this:
function example(whatever)
{
:
}
And I don't think it's appropriate to force everyone who uses your language to adhere to the same style. If you want to enforce a style, just encode that style in the syntax of the language.Also... my dyslexic coders tend to hate long lines of code, so we sometimes do lispy things like this:
const foo = whatever(
"this is a big long parameter",
( 2 + bar ) * 90,
i,
process.env.HOME || `/mnt/${process.env.USER}`
);
instead of this: const foo = whatever( "this is a big long parameter", ( 2 + bar ) * 90, i, process.env.HOME || `/mnt/${process.env.USER`);
And there's nobody's style that allows the former.Was it as bad as my spidey senses are telling me?
In terms of interviews, I prefer to give a problem decomposition question: e.g. class design of a real-simple real-world system based on rough requirements
Primary goal for me in interviewing is to
1. Filter out candidates which lack the skill or willingness to get things done
2. Identify potential red flags which would be a risk. Most red flags are communication or attitude. Knowledge gaps can be filled.
Things I'm testing for:
* able to follow rough instructions top sequentially break down problem into multiple layers of abstraction
* naming - unambiguous and conveying purpose
* attitude - easy to collaborate with? do they appreciate/enjoy new challenges?
I'm certainly not perfect but I have a pretty high degree of confidence on sorting most people's level and fit based on a 1 hr interview
> junior (IC1): difficulty separating concerns / naming, struggles with use cases. Needs to be very coachable.
> junior-to-mid: has some experience, takes feedback well. needs a lot of coaching. Gaps in knowledge. Would need mentorship
> mid (IC2): competent at programming/data structures. Able to implement a specification but need coaching turning requirements into specification. Thoughts or code may not be very organized. mid-senior: organized. Makes fast progress. Needs minimal coaching to finish problem before time. Cares about naming and names things well.
> senior (IC3): able to solve problem quickly (30 mins to finish problems which would take 1 hr for mid-level). Intuitively/quickly understands problem without need for much coaching.
> lead/principal/architect (IC4): IC3 with greater maturity. Make take longer than IC3 because of greater detail and thought. Spends extra time clarifying requirements, testability and testing. Very thorough yet able to complete within time allotted.
I used to always be on the lookout for places where I could build something better than what's out there. And almost every one of those became a liability more than an asset.
In the industrial revolution and surrounding time periods there was a big shift from made to fit to made to measure.
Previously you'd have a screw you made specifically for this project, all that mattered was that it fit the threads. Now you have an M3 screw, and what matters is that it fits any M3 nut.
I always go for made to measure. There are standards everyone knows. I no longer want to do mental gymnastics to convince myself that my 50 line script was a better choice than Ansible.
This is a super common misunderstanding for recent CS grads. School projects being well defined and fully spec-ed can make newgrads expect that's what their work ought to be. Good PMs and TPMs are super valuable, but their value doesn't come from spec-ing work or anything like that. A good engineer always talks directly to the user, and a good PM works to facilitate that
I agree but is it just me that gets annoyed by class files, jars and working with the jdk. I am sure it is because I am a novice but with python I just run the script with C I build and run the executable. Java is a little weird and in between, it seems well thought out in this regard and for good reason too but imo that is the only thing that annoys me about it. .NET and C# is similar for example but I get an exe not a binary that needs an interpreter (although it does need the right runtime installed)
Most of these sound good but this one floored me. Beyond the occasional architecture whiteboarding discussions I dont see much value.
Ive also seen it badly used plenty too - e.g. hand drawn diagrams of a tech stack that were scanned in and put on the wiki and were out of date by the very next day.
Definitely. I’ve seen this proven again and again.
Functional programming has some really solid ideas.
Anyway... +1. Functional Programming has some really solid ideas. (assuming you were saying you flipped over to this viewpoint and not away from it.)
Wait, what?
- Asking them for project priorities and waiting while they asked management for the answer.
- Giving them project updates and then listening to them repeat those updates verbatim during the next day's meeting with management, that I was also in.
- Asking for their opinion on how something should work, just because I didn't want 100% of the blame when management didn't like a decision.
And honestly, saying they had no value-add is being generous. The majority of the examples listed above were time sinks.
Of course there's the occasional good PM, but they were very rare.
As a prior PM, I agree. We're there to help management, not the engineer. The engineer does not care, and it does not raise efficiency. I always felt bad about this, and went back to engineering.