I used to try to think ahead, plan ahead and "architect", then I realized simply "getting something on paper" corrects many of the assumptions I had in my head. A colleague pushed me to "get something working" and iterate from there, and it completely changed how I build software. Even if that initial version is "stupid" and hack-ish!
I think it is common for a programmer to just start programming without coming up with any model, and just try to solve the problem by adding code on top of code.
There are also many programmers who go with their first “working” implementation, and never iterate.
These days, I think the pendulum has swung too far from thinking about the program, maybe mapping it out a bit on paper before writing code.
1. Get it working.
2. Get it working well.
3. Get it working fast.
This puts the "just get it working" as the first priority. Don't care about quality, just make it. Then, and only once you have something working, do you care about quality first. This is about getting the code into something reasonable that would pass a review (e.g., architectually sound). Finally, do an optimization pass.
This is the process I follow for PRs and projects alike. Sometimes you can mix all the steps into a single commit, if you understand the problem&solution domain well. But if you don't, you'll likely have to split it up.
Sometimes I think about code structure like a sudoku where you have to eliminate two possibilities by following through what would happen. Writing the code is (to me) like placing the provisional numbers and finding where you have a conflict. I simply cannot do it by holding state in my head (ie without making marks on the paper).
It could definitely be a limitation of me rather than generally true.
But an easy example is "just build the single player version" (of an application) can be worse than just eating your vegetables. It can be very difficult to tack-on multiplayer, as opposed to building for this up front.
I thought it was a masterpiece of abusing the C pre-processor to ensure that all variables used for player physics, game state, inputs, and position outputs to the graphics pipeline were guarded with macros to ensure as the (overwhelmingly) single-player titles continued to be developed that the code would remain clean for the two titles that we hoped to ship with split-screen support.
All the state was wrapped in ss_access() macros (“split screen access”) and compiled to plain variables for single-player titles, but with the variable name changed so writing plain access code wouldn’t compile.
I was proud of the technical use/abuse of macros. I was not proud that I’d done a lot of work and imposed a tax on the other teams all for a feature that producers wanted but that in the end we never shipped a single split-screen title. One console title was cancelled (Saturn) and one (mine) shipped single-player only (PlayStation).
We should definitely have a plan before we start, and sketch out the broad strokes both in design and in actual code as a starting point. For smaller things it's fine to just start hacking away, but when we're designing nå entire application i think the right way to approach it is to plan it out and then solve the big problems first. Like multiplayer.
They don't have to be completely solved, it's an iterative process but they should be part of the process from the beginning.
An example from my own work: I took over an app two other developers had started. The plan was to synchronize data from a third party to our own db, but they hadn't done that. They had just used the third party api directly. I don't know why. So when they left and I took over, I ended up deleting/refactoring everything because everything was built around this third party api and there was a whole bunch of problems related to that and how they were just using the third party's data structure directly rather than shaping the data the way we wanted it. The frontend took 30-60+ seconds to load a page because it was making like 7 serialized requests, waiting for a response before sending the next one and the backend did the same thing.
Now it's loading instantly, but it did require that I basically tear out everything they've done and rewrite most of the system from scratch.
Business requirements != programming requirements/features.
Very often both the business requirements and programming requirements change a lot since unless you have already written this one thing, in the exact form that you are making it now, you will NEVER get it right the first time.
You're going to write the "stupid code" to get things out the door, get promoted and move on to another job, and then some future engineer has to come along and fix the mess you made.
But management and the rest of the org won't understand why those future engineers are having such a hard time, why there's so much tech debt, and why any substantial improvements require major rework and refactoring.
So the people writing the stupid code get promoted and look good, but the people who have to deal with the mess end up looking bad.
Depends on what you do. If you build a network protocol, you'd better architect it carefully before you start building upon it (and maybe others do, too).
The question is: "if I get this wrong, how much impact does it have?". Getting the API of a core service wrong will have a lot of impact, while writing a small mobile app won't affect anything other than itself.
But the thing is, if you think about that before you start iterating on your small app, then you've already taken an architectural decision :-).
Yes and no, depending on how dependent you become on that first iteration, you might drown an entire project or startup in technical debt.
You should only ever just jump in if:
A) it's a one off for some quick results or a demo or whatever
B) it's easy enough to throw away and nobody will try to ship it and make you maintain it
That said, having so much friction and analysis paralysis that you never ship is also no good.
That said, it takes quite a bit of practice to become good enough at refactoring to actually practice that.
Basicaly, do you have a good foundation to build from. With more experience, you can build a better foundation.
"Just make it exist first. You can make it good later."
Amusing coincidence. I also wanted to be a rock star, or at least a successful working musician. My mom also talked me out of it. Her argument was: If there's no way to learn it in school, then go to school anyway and learn something fun, like math. Then you can still be a rock star. Or a programmer, since I had already learned programming.
So I went to college as a math major, and eventually ended up with a physics degree.
I still play music, but not full time, and with the comfort of supporting myself with a day job.
> I still play music, but not full time, and with the comfort of supporting myself with a day job.
Some people say;
Pursue your dream or you will regret it.
This is said by people who regret their own choices.Other people say;
Don't make your dream a job, because all it will
be is a job and no longer special.
This is said by people who had misconceptions about what pursuing their dream actually entailed.I say;
Happiness is found in neither a dream chased nor a chosen
profession. It is instead a choice we make each day in
what we do, in how we view same, and if we allow
ourselves to possess it.
What constitutes each day is immaterial.
But that's just me.Some want to live on the seas. They can be perfectly happy as a sailor, even if poor and single.
Some want a family, educated children, respect. They would likely need a nice house, enough resources to get a scholarship, a shot at retirement. This is obtainable working in public service, even without money.
But most have multiple dreams. That's what makes things complex. The man who wishes for a wife but also wishes to be on the seas will find much fewer paths available. Sailors also don't generally get respected by most in laws.
To mix the two, they try to find the dream-job. Perhaps work for a big oil company and be 'forced' to go offshore.
Eventually people learn that desire is suffering in some form and cut down on the number of dreams. They may even see this as mature and try to educate others that this is the way. Those who have kids often are forced to pick kids as the dream. So there's a selection bias as well.
"Don't put one foot in your job and the other in your dream, Ed. Go ahead and quit, or resign yourself to this life. It's just too much of a temptation for fate to split you right up the middle before you've made up your mind which way to go".
The rest of those sayings are just for us plebs that have to rationalize working 40-60 hours a week.
I'm inspired by the quote from Pablo Casals when he was in his 90s. They asked him why he still needed to practice, and he said: "Because I'm finally beginning to see some improvement."
Never too late to try stuff out of course, but very little beats structured higher ed education in relatively small classes (think there was only about 24 people in the robotics major?)_
It shouldn't be hard to go beyond what almost all universities provide.
https://sebastianhetman.com/why-quantity-matters/
Do stuff, and you learn stuff. Go play.
The quantity group has a trivial way to "hack" the metric. I can just sit there snapping photos of everything. I could just set up a camera to automatically snap photos all day and night. To be honest, if I'm not doing this at a stationary wall there's probably a good chance I get a good photo since even a tiny probability can be an expected result given enough samples.
But I think the real magic ingredient comes from the explanation
> The group never worried about the quality of their work, so they spent time experimenting with lighting, composition, and such.
The way I read this is "The quantity group felt assured in their grade, so used the time to be creative and without pressure." But I think if you modified the experiment so that you'd grade students on a curve and in proportion to the number of photos they took then the results might differ. Their grade might not feel secure as it could take just one person to communicate that they're just snapping photos all day as fast as they can. In this setting I think you'd have even less ability to explore and experiment than the quality group.I do think the message is right though and I think this is the right strategy in any creative or primarily mental endeavor (including coding). The more the process depends on creativity the more time needs to be allocated to this type of exploration and freedom. I think in jobs like research that this should be the basis for how they are structured and effectively you should mostly remove evaluation metrics and embrace the fundamentally ad hoc nature. In things like coding I think you need more of a mix and the right mix depends highly on the actual objectives. But I wanted to make the above distinction because I think it is important if we're trying to figure out what those objectives are.
“Enjoy writing it, it doesn’t have to be nice or pretty if it’s for you. Have fun, try out that new runtime or language.”
It doesn’t have to be nice or pretty EVEN if it’s NOT for you. The value in prototyping has always been there and it’s been very concrete: to refine mental models, validate assumptions, uncover gaps in your own thinking (or your team’s), you name it.
Unfortunately it feels that the pendulum has swung in the completely opposite direction. There’s a lot of “theatre” in planning, writing endless tickets and refining them for WEEKS before actually starting to write code, in a way that’s actively harmful for building software. When you get stuck in planning mode you let wrong assumptions grow and get baked in into the design so the sunken cost keeps rising.
Simply have a BASIC and SHARED mental model of the end goal with your team and start prototyping. LLMs have made this RIDICULOUSLY CHEAP. But, the industry is still stuck in all the wrong ways.
> There’s a lot of “theatre” in planning, writing endless tickets and refining them for WEEKS before actually starting to write code, in a way that’s actively harmful for building software.
I'd love to have a "high paying job" where I am allowed to start prototyping and modelling the problem and then iteratively keep on improving it into fully functional solution.
I won't deny that the snowballing of improvements and functional completeness manifests as acceleration of "delivery speed" and as a code-producing experience is extremely enjoyable. Depth-first traversal into curiosity driven problem solving is a very pleasurable activity.
However, IME in real world, someone up the chain is going to ask "when will you deliver this". I have ever only once been in a privileged enough a position in a job to say "I am on it and I will finish it when I finish it... and it will be really cool"
Planning and task breakdown, as a developer, is pretty much like my insurance policy. Because when someone up the chain (all the way down to my direct manager) comes asking "How much progress you have made ?" I can say (OR "present the data" as it is called in a certain company ?) "as per the agreed plan, out of the N things, I have done k (< N) things so far. However at this (k+1)th thing I am slowing down or blocked because during planning that-other-thing never got uncovered and we have scope-creep/external-dependency/cattle-in-the-middle-of-the-road issue". At which point a certain type of person will also go all the way to push the blame to another colleague to make themselves appear better hence eligible for promotion.
I would highly encourage everyone to participate in the "planning theatre" and play your "role".
OR, if possible start something of your own and do it the way you always wanted to do it.
Put another way, refining tickets for weeks isn't the problem; the problem is when you do this without prototyping, chances are you aren't actually refining the tickets.
Planning stops when you take steps that cannot be reverted, and there IS value in delaying those steps as much as possible, because your project then becomes vulnerable to outside risk. Long planning is valuable because of this; it's just that many who advocate for long planning would just take a long time and not actually use that time for planning.
Prototypes (start ups) rarely have the luxury of "getting it right", their actual goal is "getting it out there FAST to capture the market (and have it working enough to keep the market)"
(Some - apologies but I'm not a game dev enough to be able to say what types this applies to) Game devs - they're more or less build it, ship it, and be done with it, players tend to be forgiving of most bugs, and they move on to the next shiny thing long before it's time to fix all the things.
Once the product has traction in the market, and you have paying customers, then it's time to deal with the load (scale up) and bugs, I recall reading somewhere that it's probably best to drop the start up team, they did their job (and are now free to move on to the next brilliant idea), and replace them with a scale up team, who will do the planning, architecting, and preparation for the long term life of the software.
I think that that approach would have worked for Facebook (for example) they had their PHP prototype that captured the market very quickly, and (IMO) they should have moved through to a scale up team (who could have used the original code as a facade, strangling it to replace it with something funky (Java/C++ would have been what was available at the time, but Go would be what I would suggest now)
I'm curious who is in these kinds of jobs. Because I've never seen this in practice.
I used to get hung up on things like doing a loop when a ternary operator would work. "Somebody is going to see this and be rude about it." But sometimes you write code how you're thinking about the problem at the time. And if you think of it as a loop, or a series of if statements, or whatever, do it that way.
If it makes you feel better, note it in a comment to revisit later. And if somebody is rude about it, so what. It's not theirs, it's yours.
The first one that comes to mind relates closely to naming. If we think about a program in terms of its user facing domain, then we might start to name and structure our data, functions, types too specifically for that domain. But it's almost always better to separate computational, generic data manipulation from domain language.
You only need a _little bit_ more time to move much of the domain specific stuff into your data model. Think of domain language as values rather than field names or types. This makes code easier to work with _very quickly_.
Another stupidity is to default to local state. Moving state up requires a little bit of planning and sometimes refactoring and one has to consider the overall data model in order to understand each part. But it goes a long way, because you don't end up with entangled, implicit coordination. This is very much true for anything UI related. I almost never regret doing this, but I have regretted not doing this very often.
A third thing that is unnecessarily stupid is to spread around logic. Harder to explain, but everyone knows the easy feeling of putting an if statement (or any kind of branching, filtering etc.) that adds a bunch of variables somewhere, where it doesn't belong. If you feel pressed to do this, re-consider whether your data is rich enough (can it express the thing that I need here) and consistent enough.
I once worked on a Perl script that had to send an email to "Harry". (Name changed to protect the innocent). I stored Harry's email address in a variable called "$HARRY".
Later on a second person (with a different name) wanted to get the emails as well. No problem, just turn the scalar into an array, "@HARRIES".
I thought it was very funny but nobody else did.
Oh, this sort of "dumb" code. That is just exercise. It bothers me that in this field we don't think we should rehearse and exercise and instead use production projects for that.
Actual dumb code is one that disregards edge cases or bets on things being guaranteed when they're not.
They write a maze algo in any new language they learn just to learn bits of the language.
Whether an idea is good or not can often only be judged when it becomes more concrete. The actual finished project is as concrete as it gets, but it takes time and work to get there. So the next best thing is to flesh it out as much as possible ahead and decide based on that whether it is worth doing it that way.
Most people have the bad habit of being too attached to their own ideas. Kill your darlings. Ideas are meant to be either done, shelved or thrown into the bin. It doesn't do any good to roll them around in your head forever.
Therefore, if you push yourself to the limit of your abilities to create the most clever code you can, you won't be able to debug it.
> Therefore, if you push yourself to the limit of your abilities to create the most clever code you can, you won't be able to debug it.
If only advocates of LLM-based code generation understood this lemma.
He said it with a very specific idea in mind, and like most of software engineering "laws", if you know enough to know when to apply it, you don't need the law.
Writing stupid code is like walking to the shop. You're not going to improve your marathon time, but that's not the point. It's just using an existing skill to do something you need to do.
But you should also study and get better at things. If you learnt to cycle you could get to the shop in a third of the time. Similarly, if you learn new languages, paradigms, features etc. you will become a more powerful programmer.
I didn't know about Deno and streams, but this looks fine
const file = await Deno.open("huge-quotes.txt");
const quotes: string[] = [];
await file.readable
.pipeThrough(new TextDecoderStream())
.pipeThrough(new TextLineStream())
.pipeTo(new WritableStream({
write(line) {
quotes.push(line);
}
}));But for strategic decisions, having a well-researched document (a PRD or similar) helps as a starting point for iteration, and the approach you take will be influenced by your team's culture.
Also, 2010 was just yesterday my young friend :)
Like the original was: Go ahead, write the "stupid" code, I dare ya!
struct S { int a; int b; int c; int d; int e; /* about 15 more members */ }
so I wrote
const auto match_a = s.a == 10; const auto match_b = s.c == 20; const auto match_c = s.e == 30; /* about 15 more of these */ if (match_a && match_b && match_c) { return -1; }
Turns out compilers (I think because of the language) totally shit the bed at this. It generates a chain of 20 if-else instead of a mask using SIMD or whatever. I KNOW this is possible, so I asked an LLM, it was able to produce said code that uses SIMD.
I worked on a research topic in grad school and learned about holes in files, and how data isn’t removed until the last fd is closed. I use that systems knowledge in my job weekly.
A tip. Kernel development can be lonely, share what you are working on and find others.
That's why. If all codes in a project are stupid, there's no stupid code indeed relatively.
Go read Linux kernel mailing list.