Hacker News new | ask | show | jobs
by ashrk 2758 days ago
No (or long-broken) tests and no easy (at least partially automated and otherwise documented) way to build and/or run the code locally are the norm for others' codebases I've inherited. Those two things qualify it as "awful" I'd say, all on their own. Especially in languages like Ruby or JS where you're practically crippled in an unfamiliar codebase without tests and/or being able to poke around in the running application. Both being absent is a near-perfect signal there are tons of other problems, of the screw-up sort and not the we-had-to-cut-corners-for-actual-reasons sort.

Comically bad security holes, actual or implicit (framework-created) SQL queries in a for loop for no good reason (well, because the developer had no idea how to use SQL, and hadn't developed an appropriate allergy to unnecessary network communication, are probably the reasons), hilariously misguided attempts to fix the wrong thing to improve performance ("we'll move it to jruby!" well sure but your actual problem is you chose an inappropriate database and are using it poorly, but at least you made your build pipeline worse for marginal benefits, so there's that). Ruby in your node project just to run a very basic task queue (!?). Et c., et c., c-beams off the shoulder of Orion, tears in the rain, et c.

Inheriting something even half-decent is really, really unusual. I wouldn't bad-mouth half-decent. I just rarely see it.

[EDIT] this probably varies a great deal by platform. I imagine it's a bit less common to have a total train-wreck of an iOS app, to pick another platform I've worked on, than something server-side.

3 comments

> we'll move it to jruby!

Hahahah Oh man, that's great. BTW, we had people who did this. Our performance is 2x better with half the servers after we undid this "optimization" at our org. I suggest benchmarking first.

Awesome! Here are some practical problems that used to give me headache, can you give us some best practices on how deal with these situations?

- untyped languages that run through A LOT of layers of wrappers/indirections to get things done; How do you keep track? (with typed languages it is easier to figure where the next step is going to; though code in java likes to do this trick - it then obscures the details by using some container mechanism, like spring, etc) Do you manage to reduce said numbers of indirection without breaking things?

- microservices like to do that a lot (going through n levels of services just to get to the target server - with different languages and RPC protocols involved). I had once spent a week to add a frigging attribute. Any tips here?

- gui code often turns into a mess (like when it takes it short of a week to add a frigging detail); how do you deal with that?

- how do you deal with the problem of missing context? (when trying to read through the source and there is nobody there to explain any rational of what is going on?) (for me knowledge of other systems often helps to bridge the gap, are there any more empirical approaches?)

For me these things are hard, though i manage (sort of). How do you do?

In order—and mind I'm not like amazing at this so take none of this as best practice:

- Stepping through with a debugger. Static analysis call graphs in stricter languages[0]. Runtime call traces and visualization in dynamic languages. Often you can find some tool for it[0]. If it's that bad you're probably not gonna be able keep your head wrapped around it long term, so you'll be falling back on those tools often until/unless you refactor.

- Similar. Find a way to trace requests (you are really, really gonna want such a tool when things go wrong anyway if you're microservice-heavy) and record what happens, if it's so confusing you can't figure it out otherwise.

- Not much to do but isolate and replace piece by piece or screen by screen as the opportunity presents itself. Hardest part of this is that your new stuff may well look and work better than the old, which can lead to UI inconsistency, so it ends up being an exercise in inconsistency tolerance or management.

- Just gotta leverage tools to tell you more about the codebase than you might get from just looking at it (as in point 1) and do a lot of probably-boring and hard-to-measure difficult work. If it's still under development that means someone ought to be able to tell you what each largish part (feature, screen, section) should do, at least from the perspective of the end user, which may be useful. If no one can tell you that then why are you maintaining it, right?

Jumping off that fourth point: a great preventative measure for ending up in this position (or putting someone else there) is to make sure your code is written such that tools can more easily tell the reader facts about the project. Typescript over raw JS, that kind of thing. Static types are communication. They're communication that can be verified by a machine to be correct, more or less. They're great. Tests fail or pass. They tell you what the test writer expected to happen, and whether that's happening. They are communication. If all your comments get stripped and no-one updates the docs for two years and you get hit by a bus your types and tests still communicate. Even outdated test suites aren't totally useless. And static types pretty much can't go stale like docs or tests can.

[0] https://github.com/TrueFurby/go-callvis

[1] https://github.com/jamesmoriarty/call-graph (no endorsement, haven't used it, just an example.

Being able to solve those problems like "it ain't no thing" is part of being a senior developer.
Sure, and man are the easy wins satisfying. Fixing shitty data access methods/patterns is one of the easiest ways to get a "WOW!" out of a client or product owner if you're stuck in the low-visibility silo of backend dev[0]. I've just been repeatedly surprised over the years at how very, very many people are making real money, and consistently finding work, yet don't seem to know which way is up. And I don't mean greenhorns, though often they're given comical levels of responsibility (typically by the cash-strapped or cheapskates) resulting in some real messes, which has a similar effect to when an incompetent "lead architect" or an experienced team nonetheless without a clue between them is set loose, which is just as common.

I harbor no ill will toward these folks. Hell, selfishly, I'm glad there are so many. Having half an idea what you're doing is guilt-inducingly easy, pays amazingly well—especially after the confidence-boost and resulting swagger and negotiating attitude that comes with seeing this kind of thing over, and over, and over, for years on end—and it's disturbingly easy to be a or the "smart one" in the room when you're kind of a dummy, in fact.

[0] Dear backend devs: if you don't simply love backend work and/or if you aren't very well appreciated compensation-wise, and especially if you have long-term career aspirations that involve shifting more toward the biz/architect/management side for the extra social status and higher late-career pay (in most of the industry outside the huge West-coast tech companies, anyway), consider moving to more high-visibility pastures—though ideally not web frontend, as, incredibly, it's still a trash fire and comp is so-so, mostly. Unless you just like trash fires, which some people do.

"Unless you like trash fires, which some people do."

I'm dying.

What are higher visibility pastures that is not web frontend?
Mobile, probably a lot of data-analysis jobs provided you get to present results. Desktop work if you can get it. Non-traditional UI like voice. You can spend (what will seem like) a silly amount of time selling what you've done in backend work, even, it's just a harder path.