It's possible that people's experiences are different to yours because you work on a specific type of software and other people work on other specific types of software.
It's possible that people's experiences are different to yours because you work on a specific type of software and other people work on other specific types of software.
At many big tech companies I've worked out, an abstract design proposal precedes any actual coding for many tasks. These design proposals are not about how you lay out code or name variables, but a high level description of the problem and the general approach to solve the problem.
Expressing that abstract thinking requires writing code but that's the "how" - you can write that same code many ways.
Which points at a pretty substantial limitation of LLM coding...
Correctness is not embedded in software. It's embedded in the real world.
I don’t think there’s anything where the first step is writing code. It’s like saying the first step of solving a math problem is writing down equations.
The whole argument behind TDD is that it's easier to write code that verify something than actually implement the code. Because it only have the answer, not the algorithm to solve the question.
So for any code you will be writing, find the answers first (expected behavior). Then add tests. The you write the code for the algorithm to come up with the answer.
Static typing is just another form of these. You tell the checker: This is the shape of this data, and it warns you of any code that does not respect that.
Regular static typing (assuming you don't go to the level of dependent types or something) has the advantage that it is extremely quick to write, compared to an equivalent test. So even if you get the types wrong 90% of the time on the first pass, you've still wasted only a trivial amount of time (consider how long it takes to write "int foo(int x)" versus tests that fail if "foo(x)" accepts anything except an int, or returns anything except an int for any int input - and how much work you'd throw away if you later realize you have to replace int with string).
Why wouldnt you decide what your code should do before writing it?
And the code changes were pure waste too.
That's why it's important to try and reduce the risk of building to the wrong requirements wherever possible - fail fast and often, use spikes and experiments, use lean, etc.
It's why it's important to reduce the cost of writing tests and code as much as possible too.
Writing the test itself and showing bits of it to others can actually help uncover many requirements bugs too. That's called BDD.
However, if it turned out it was the right thing and you didnt write a test you've just made it much harder to change that code in the future without breaking something. The cost of code changes went up.
Yes, I said so as well. Though it's also important that the code changes are likely to be the thing that reveals that the feature is misunderstood/badly specified. Lots of people can take a working feature and tell you if it addresses their problem. Much fewer can look at a set of unit tests and tell you the same.
> However, if it turned out it was the right thing and you didnt write a test you've just made it much harder to change that code in the future without breaking something. The cost of code changes went up.
Very much debatable. If the code needs to change because requirements themselves change in the future, the tests that are validating the old requirements are not helpful. And many kinds of refactoring also break most kinds of unit tests too.
From my experience, unit tests are most useful for testing regression cases, and for validating certain constrained and well defined parts of the code, like implementations of an algorithm. They're much less useful for testing regular business logic - integration tests are a much better solution for those.
Not OP, but I find this a very good question. I've always found that playing with the problem in code is how I refined my understanding of the problem. Kind of like how Richard Feynman describes his problem solving. Only by tinkering with the hard problem do you really learn about it.
I always found it strange when people said they would plan out the whole thing in great detail and code later. That never worked for me, and I've also rarely seen it work for those proposing it.
It may be because I studied control systems, but I've always found you need the feedback from actually working with the problem to course correct, and it's faster, too. Don't be scared to touch some code. Play with it, find out where your mental model is deficient, find better abstractions than what you originally envisioned before wrestling with the actual problems.
Not everything has been solved for ten thousand years.
Tax rules depend on an interaction between national and local laws that can change from year to year based on the ruling government. You can't just take tax rules from ancient Rome to generate 2024 tax software for Quebec, Canada.
As is usually the truth in practice, it’s a mess, which is why I’ve seen combinations of upfront planning and code spiking work the best.
An upfront plan ensures you can at least talk about it with words, and maybe you’ll find obvious flaws or great insights when you share your plan with others. Please, for the love of god, don’t ruin it with word vomit. Don’t clutter it with long descriptions of what a load balancer is. Get to the point. Be honest about weaknesses, defend strengths.
Because enterprise corporate code is a minefield of trash, you just have to suck it up and go figure out where the mines are. I’ve heard so many complaints “but this isn’t right! It’s bad code! How am I supposed to design around BAD code!” I’ll tell you how, you find the bad parts, and deal with them like a professional. It’s annoying and slow and awful, but it needs doing, and you know it.
By not doing the planning, you run the risk of building a whole thing, only to be told “well, this is nice, but you could have just done X in half the time.” By not doing the coding, you risk blowing up your timeline on some obvious unknown that could have been found in five minutes.
Sometimes you don't have a way to get the exact answers, so you do experiments to get data. But just like scientists in a lab, they should be rigorous and all assumptions noted down.
And sometimes, there are easy answers, so you can get these modules out of the way first.
And in other cases, maybe a rough solution is better than not having anything at all. So you implement something that solves a part of the problem while you're working on the tougher parts.
Writing code without answers is brute-forcing the solution. But novel problems are rare, so with a bit of research, it's quite easy to find answers.
POCs are better for customer-facing, product management driven work. This is because they can be bad at describing what they want. There's more risk of building the wrong thing.
POCs can be okay for system design or back-end work (or really anything not involving vague asks), but chances are planning and deeper thinking will help you more there because the problems you solve tend to be less subjective. Less risk of building the wrong thing.
Kinda like scaling. Instead of going for Kubernetes, use a few VPS and a managed database to get your first customers.