13 comments

[ 4.6 ms ] story [ 43.0 ms ] thread
I've worked on some really large cross-cutting codebases, probably the largest of which are these two:

- an integrated aircraft and radar simulator that was originally written in the 80s and has code from multiple languages, added and morphed over time by many generations of different staff, and still in heavy use at least as late as when I worked on it in the 00s.

- a suite of different search engine services for a large (Alexa top 400) ecommerce product search engine and online store.

My experience after working on these huge, legacy, complex codebases is that if you need a "go to definition" feature in your editor or IDE, it's a symptom of a notably bad codebase (not just regular bad with all the usual suboptimalities that come up in corporate software, but notably worse than that).

It's particularly bad when you have to use it to chase down an eventual base implementation in a hopelessly convoluted stack of inheritance.

The pieces of software I've seen have the longest shelf life have been written with exacting and unforgiving focus on simplicity and separating units of code, in such a way that the details of an implementation are never far.

It's sort of like the code's design is a conceptual KD-tree, so conditioned on being in a sub-section of the code, you know for sure anything you depend on is nearby and not far away, and certainly not hidden in a stack of inheritance.

This is not an argument against "go to definition" capabilities in editing tools. I just take a little issue when the author says,

> "Text editors can’t and don’t have features like autocomplete, go to definition, …"

> "This usually shows up in the same discussions where Emacs is a terminal program. It also usually comes up when someone says an IDE is needed because of the features above. I don’t program without them, I think it’d be crazy to."

I use Emacs and I never want autocomplete or "go to definition" features. I hate them. In the cases when I unfortunately need them it's a symptom of exceptionally bad software, not an endorsement that these features are themselves good things generally, except in so far as they offer help in exceptionally bad codebases.

Insecticide is not a good thing generally. It's a good thing in bad cases when there's an insect problem, but if you find yourself in that situation really often, and you need insecticide so often that it becomes as familiar to you as your morning coffee, then it's a sign of a much bigger problem.

> I use Emacs and I never want autocomplete or "go to definition" features. I hate them. In the cases when I unfortunately need them it's a symptom of exceptionally bad software

I can understand wanting to be able to code without these features, but I can't understand "hating" them. I use "go to definition" not because I can't find a function definition, but because it gets me there faster. What's the moral advantage of opening a file and searching, vs hitting "M-."?

If you're just talking about the first time you need to open the file, then sure, who cares.

But I'm talking about the typical way of working when I have multiple files open and move between them or place them into multiple Emacs windows. In that case, it is SO much faster to jump based on knowing which file I intend to move to, rather than moving to a file because it happened to be the file that contained a given definition.

If I have to think, "go to wherever function X is defined" that's a drastic mental slowdown compared with "C-x b <file where I already totally know where function X is because of the code structure>".

It's similar with autocomplete, even for very long names. I am fast at typing, so the little disruption of needing to enter a visual scan mode to select the right entry from e.g. some JEDI autocomplete option is a huge time cost for me added up over all the times I would write out e.g. function or class names in my code.

They are both the sort of thing that superficially sounds like it would save you time or cognitive load, but they don't, unless you happen to be in the pathological codebase scenario I was describing and you need the tools because the project's code organization does not permit being able to reason efficiently about it from local mental caches.

I'm not sure what code completion you're using, but if it's contextual, I'm very lost as to how typing a ridiculously long name would be faster than auto completing it. Especially since once you're used to the auto complete, you can basically use shortcuts for specific things you're using in that context a lot. "BindingOperations.EnableCollectionSynchronization()?" oh, that's "bop.ecs". There should be very few options to pick from unless everything is very similarly named...
I also find autocompletion invaluable when I'm working with an API I don't interact with regularly enough to have everything memorized, mere mortal that I am. I might not remember exactly what naming convention the methods conform to, or what order the arguments need to be in, and autocompletion gets me both of those essentially for free, without having to pull up the docs for every method call.
I can agree with this — if you’re really working on a codebase that you rarely interact with, then sure, “jump to” and autocomplete features are helpful.

Over my career, the relative amount of time spent doing that is really tiny though. It doesn’t invalidate the value of autocomplete or “jump to” features for this use case, but for me it’s not common enough to matter a whole lot. Other forms of searching work just fine too.

My other comments are written regarding the case when you’re hopping around an in-house codebase, and beyond some initial burn-in, you should have a lot of working knowledge of the details.

It wouldn’t matter if the auto-completion only had one option to pick from. The visual disruption of needing to see that and hit enter or arrow down is already a time loss compared with just typing the thing.
Go to definition is insanely useful. Just because you "write good software," and are able to put everything in exactly the correct place, and find everything without it, doesn't mean that everyone else does, or the guy next to you does, or that you aren't wasting your time by not using it. Go to definition isn't just for multiple inheritance hierarchies. It can be as simple as getting to another method in the same file 50 lines away, or to a child class's method which is in exactly the right spot, and you'd be easily able to find. It's not about where it takes you, it's about the speed at which it takes you there. You press one button and you're back to getting through the control flow of the code without interrupting your thought process by having to search for a method. Press another button and you're back to where that method ends. Yes, you don't need it, but the speed that it offers you is invaluable a lot of times.
> "It's not about where it takes you, it's about the speed at which it takes you there."

But that's exactly my point. Jump-to-definition features slow you down. The only real exception is if the code's overall organization is extremely pathological and you wouldn't have a chance of mentally caching the local layout relevant for some work you're doing.

Flipping around to different files is super easy to do in any number of ways, with various shortcuts and macros. "Go to definition" features do not differentiate themselves based on that.

The difference is if you navigate to a section of code intentionally because you know it contains what you're looking for, or if you let yourself be taken to that place and then have to orient yourself around what you find.

For me anyway, the mental difference is huge. "Go to definition" means that after the quick keystrokes for invoking that feature, there will follow a noticeable slowdown while I figure out where I am.

Imagine two different ways of teleporting somewhere, like say a restaurant. In one way, you can say, "teleport me to wherever this pizza was made" but when you appear there, you won't know the prices, the etiquette, the lay of the land.

In the other way you say, "Ah, I know where this pizza was made, take me to X" and your mind is already set to the expectations and you're ready immediately.

The mere typing difference of saying "take me to X" with a few extra keystrokes instead of a shortcut keystroke for "take me to where this was made" is not relevant, plays no part in what matters for quickly moving around in the code.

I think this mental distinction, especially added up over thousands and thousands of "go to definition" invocations, is a huge thing. Your productivity takes a tiny hit each time you do the unplanned context switch.

I work with other people's code. I just don't get what are you trying to say here.

Go to definition goes to place where the the thing I am looking at and see first time in my life is defined. Had there be no go to definition, I would use something else. Go to definition still causes zero effort. Less then guessing layout, really.

I don't magically know everything about code Base I am looking at, even if written by super organized geniuses. Go to definition helps you to learn.

Discovering where function or class definitions are found for the first time ever is a necessarily rare part of software engineering. It happens when debugging some third-party tool, when joining a new team or project, etc., but the vast majority of the time you aren't doing that stuff and you are working on a codebase whose internals you're expected to have a mental model about.

When just exploring a codebase for the first time, frankly just grep would be more useful than jumping, because if you're looking for the firs time, there is no purpose to following a thread. You'd need a task or some other understanding of what you need to do. However, I do concede that "jump to definition" is a perfectly fine tool for this sort of search if it's what somebody wants.

But the situation is totally different in the vast majority of the time, when you're working in a codebase you're expected to understand already, but which you need to navigate around the architecture.

That case, which is most of the time (by definition), is not benefited by "jump to definition" except in the pathological cases when the organization of the software is exceptionally bad. Basically, if you find yourself in a situation where a "jump to definition" feature is constantly helping you, even after you have worked in that codebase a while, then it is a clear symptom of really big, pervasive problems about that codebase. It's not an endorsement of "jump to definition" features as being generically helpful.

> I mean that the inherent complexity of the task at hand doesn’t go away and might even be harder to solve with a simple language.

I see this so often everywhere. "Yes, but with this simple framework/database/language you don't have that complex problem anymore!". No, it just ignores the problem completely so eventually it will bite you and you now have to solve the complex underlying problem in the wrong domain.

Problems never go away, you just move them to a different place. The trick is which place to move them to.