Hacker News new | ask | show | jobs
by spicyusername 739 days ago
Lots of good advice in this article.

One that stuck out to me: Don’t implement something unless you need it right now

This is a constant battle I fight with more junior programmers, who maybe have a few years of experience, but who are still getting there.

They are often obsessed with "best-practices" and whatever fancy new tool is trending, but they have trouble starting with the problem they need to solve and focusing on the minimum needed to just solve that problem.

10 comments

This is in context of a one person team. The next advice makes this evident:

> Remember that you can always rewrite any part of your game/engine later.

This isn't the case in medium to large organizations. Usually you will just move on and rarely have the time to revisit something. This is unfortunate of course, but it means you need to build things properly the first time around and make sure it won't have a chance to create bugs or side effects. I have worked in too many codebases were people feel the need to rush new features, and then it creates a minefield of a codebase where you have to manually check every feature and have the entire application in context when changing something small.

I agree with this. In a large organization, if you have risen to a level where you are being relied upon at a regular intervals, it is imperative that you have a well architected solution that you can readily change and this is where you separate your program from spaghetti code to something useful. Sure, its nice to write unmaintainable junk when toying around, but I to have seen too many codebases where people were just throwing features in without thought and it causes the program to become way too constrained to only a specific problem domain and it becomes inflexible for solving new problems (to the point you have to re-write nearly everything from scratch).
The contrast to this is to put in tickets specifically for refactoring and reorganization, but I've rarely seen that work since they often don't have any sweetener included to encourage e.g. product organization to sign off on the work.
Yeah, it's a shame, I often have to do this type of crap in my off time. However, having a well architected app significantly reduces these types of massive undertakings. Doing it right the first time has its advantages. Then again, weighing it against delivering early and other important aspects has its merit too. It's just a double edged sword unfortunately that you often have to walk in this industry.
Most importantly, you need to get interfaces right. The right interface that allows the caller to express their underlying intent allows you to rewrite bits of the implementation as-needed without forcing a rewrite from your downstream consumers.
> it means you need to build things properly the first time around and make sure it won't have a chance to create bugs or side effects. I

yes! and this means you need to know everything about what you're building upfront! so now you have to do waterfall, but hide all the actual effort and pretend you're just figuring it out in the moment because agile.

I agree, and would add that agile is bad because it's used to iterate along the product vertical, so first create an MVP, then add features. Instead it could be used to iterate from a technical standpoint, first adding the helper functions, then the interfaces, then some of the core functionality.. but the problem there is that most orgs prefer immediate "business value" at the cost of long term good engineering. In some cases, when the application is meant to be short-lived this makes sense, but more often than not I've seen teams suffer from this approach, not realizing they have been digging their own hole for years.
> so first create an MVP, then add features.

It seems to guarantee technical debt starts to build as soon as possible and as fast as possible!

(I.e. I'm referring to agile as it is practised, not the true scotsman's agile. We apply the same rigour towards socialism.)

I agree. I should probably clarify that in the article. It’s much easier to write “throwaway” code when you’re working alone and when you’re doing it just for fun. This advice was probably aimed at people who tend to overthink things in these kinds of situations and spend years implementing things in the “right” way, which tends to slow them down considerably instead.
I guess what you said is right - the starting code and the later refactored code for reuse is and should be different. You experiment a lot …

Anyway after reading I still struggle. All these discussion is about game. I am more on simulation like Game Of life but in 3d (2d done using sdl and except iOS-mixer, one code base can run on most platform I care about). And possibly VR. May be back to unity and … learn so much using c/c++ using sdl especially just pass grenderer and gwindow around and just use function pointer. I guess 3d is out of reach based on reading your article. Very enjoyable reading nevertheless.

A large organization is likely to have both a separation between experiments and the "actual" game engine, containing the churn of throwaway code in order to build on good foundation, and significant experience and reuse from their previous game engine, constraining (possibly for the worse) the experiments and rewrites.
My reading is slightly different. It is not about a 1p team, but about a 1p team just starting and learning. Once finish learning you still have to go back and refactor for future maintenance. He has to reuse these codes as part of an engine, just like a larger team.
Yes, over engineering solutions is a constant thorn in my side. The end result is you get a lot of additional complexity for basically no benefit. In my experience, if new requirements do come down the pipeline at some later time, the generic solution you previously built will be completely inadequate and will need to be redone anyway. Solve the problem in front of you, not some unknown future problem.
On the other hand, often a "quick and dirty" solution unexpectedly becomes the foundation of a lot of other stuff, which can be a persistent pain in the future, while the cost of rewriting the whole thing increases the more other stuff depends on it.

There are two poles: On the one side is making everything an unmaintainable pile of quick hacks, on the other side is over engineering everything. You want to be somewhere in the middle.

Totally agree, it needs a careful balance. But is it possible to know when to use what?

Anecdote: A couple years ago I migrated a 1M+ LOC application from one database to another, cutting the cloud hosting costs practically in half. This was absolutely massive for the company. It took a bit less than a year, but was possible because the people who designed the application didn't write any hardcoded queries, and everything went through an ORM or a query builder. Everything was built to an interface, even though they all only had one implementation. This turned out to be absolutely critical when we had to add the second implementation (and because we needed to maintain compatibility for the original implementation at the same time due to some one-off contracts) and the migration would not have been worth it cost-wise if the proper architecture wasn't in place from the start.

Now take the same application. It has tons of complicated architecture stuff implemented in house. Parts of it are definitely overengineered. It's been apparent when doing other upgrades and finding out "oh... this doesn't work because it's using a custom JSON serializer" that the choice to do that was poor.

In the end, I think the right choice for that application was the complex design that allowed those big migrations. For others, they might never do that and only get hit with downsides.

What it needs is likely a good vision and someone smart to know what to abstract and what to not abstract such that it will have been a good choice 15 years down the road.

ORMs and query builders can also constrain the implementation from a performance and flexibility perspective though.

Your anecdote and reflections are all absolutely true in my experience, but I've found just as many problems with off-the-shelf ORMs.

The key is the clean boundaries/interfaces and keeping things simple - leaving the bridge intact so that your enemies may retreat.

In my 25-year career I’ve experienced easily fifty times as much pain from underthought and hastily-implemented systems than I have from overengineered ones.

Sure, build user-facing stuff cheaply and ready to be thrown away as requirements change. But libraries, tooling, and infrastructure that everything else is built on top of should have thought and care invested in it. This will return dividends.

We spend so much time as an industry worried that somebody might put five percent more effort into a project than it’s worth, that the overwhelming majority of engineers have no idea whatsoever of how to build something to be reliable, to be easily iterated on, and to last. And so we spend tens of times more effort trying to build on top of a mountain of crap.

> On the other hand, often a "quick and dirty" solution unexpectedly becomes the foundation of a lot of other stuff

This is how Access "databases" and Excel spreadsheets often become mission-critical in SMBs. "We'll deal with needing a database server down the road..."

> There are two poles

I'm not sure I agree. I think you can write a simple-but-not-hacky implementation that does only what is necessary, and is also easy to maintain and extend later.

Of course, you never really achieve that ideal, but you can keep getting closer to it as you gain more experience.

"Easy to extend" arguably means including more abstraction and making things more general than they need to be merely for solving the current problem. There is a trade-off here.
"Easy to extend" for me means keeping things decoupled (as opposed to adding extension mechanisms etc.).

Of course there's a tradeoff (there always is), I think my point was just that this option doesn't really seem like it fits between "quick and dirty" and "over engineered", though I suppose that probably depends on how you picture that continuum ;)

The only thing I'll add is: do the simplest thing that can possibly work, but no simpler.

Don't back yourself into a corner. If a solution will block you from doing the right thing later, maybe some additional engineering may be needed after all to ensure you can continue to iterate. In large companies, inertia is enormous. Once a solution is in place and gets replicated, backtracking may become a herculean task.

> They are often obsessed with "best-practices"

Tell them YAGNI is also a best practice :D

>starting with the problem they need to solve and focusing on the minimum needed to just solve that problem

To be fair if held strictly to these principles their work would revolve mostly around gluing together various Apis, services and sometimes adjusting already written software to company's needs. So I'm not surprised they are using every possible opportunity to write something here and there, this menial digital plumber's work takes its toll and one needs to try to squeeze in something a bit more enjoyable from time to time just to keep sanity in place a bit longer

Yeah, I am no longer a junior engineer.

When I WAS the bane of my existence was senior and staff engineers hoarding all the fun, new work for themselves and forcing me into "bitch work" where I'm just cleaning up old, broken code and handling bugs. I finally got promoted because I explicitly ignored my manager to deploy changes that were needed for my company, and that was what finally got me promoted. Of course, at that point, I had been looking for a new job and just left instead of taking the shitty, bottom-of-band promo increase.

It's awful for promotion chances, and it forced me to quit.

Not just politics, I almost feel it can hamper you as an engineer. If you just do basic plumbing for 10 years, are you really an engineer with 10 YOE or one with 2YOE 5 times over because everyone needs a plumber?

2-3 years seems to be where that valuable plumbing experience starts to plateu. I don't know how engineers these days that move companies every other year even get a chance to grow when you're doing a soft restart at every job hop.

> When I WAS the bane of my existence was senior and staff engineers hoarding all the fun, new work for themselves and forcing me into "bitch work" where I'm just cleaning up old, broken code and handling bugs.

Oh how I relate to this...

That's why these days I go out of my way to "save" interesting work for my junior colleagues and have them grow through it.

> Don’t implement something unless you need it right now

Useful advice to start with. It’s a rule that experts are allowed to break though.

> [Junior programmers] are often obsessed with "best-practices" and whatever fancy new tool is trending, but they have trouble starting with the problem they need to solve and focusing on the minimum needed to just solve that problem.

I’ve observed/experienced the same exact thing [0]. I think it’s due to a combo of (1) not knowing what “the right way” to do things are and (2) thinking it’ll make your peers perceive you as more knowledgeable or advanced if they see you writing “best practices” code. Not to mention that sometimes the simpler solutions are so simple, they make you feel like you’re not a real software engineer. I usually just do my best to help them understand that simple solutions are okay, especially since (1) I’ve been there myself when I was in their shoes and (2) I know they have good intentions.

[0]: https://news.ycombinator.com/item?id=40527071

I've had to fight this battle with a Principal Engineer. He created a framework encrusted with DI malarkey that turned building a standard Koa microservice into a twisty maze of module dependency declarations and configuration objects. Just complexity on top of complexity, all for the sake of what, pulling some rows out of a database?
Definitely agree.

But there’s a fine line between implementing things but the difficulty being understanding long term vision and making sure short term improvements don’t actively work against the ‘ideal’. Kind of hard for newer programmers to get a good sense of system design.

Yep. I attribute YAGNI to a lot of my quickest prototypes and some of my best code.
> This is a constant battle I fight with more junior programmers

This can go both ways, as senior devs can just want to use what they know.