So to the devs who don't use these tools, how do you do it? Do you just remember every type and field in a codebase? What does your flow look like?
One example is that I cannot live without the language server go-to-definition feature. What do you do if you need to look up the definition/implementation of some function which is in some other file?
I recently had to switch to a project in Ruby and the LSP was not working it was frustrating but I can’t say that when it started working the next day that it helped me that much more. Maybe like 10% improvement?
Command F, command shift F in most IDEs to look up the definition if LSP isn’t working just search for name and append the class, method, function definition syntax. Like “function getUser” or whatever.
I asked him how he managed to code, and he replied with something that stayed with me: a good programmer should organize software in such a way that every piece of code has a clear and logical place. The organization should be so intuitive that anyone could build a mental model of the structure and navigate it easily, even without seeing it.
It felt like something out of a Yoda or Mr. Miyagi lesson. Skeptical, I asked his colleagues if he was truly able to code or if he was just exaggerating. To my surprise, they told me not only was he capable, but he was the best programmer they had ever worked with. They said no one else came close to writing code as organized as his.
That conversation changed my perspective. Ever since, whenever I’m unsure where to place new code, I don’t think about DDD or any specific methodology. Instead, I try to follow the logic and structure of the project in a way that feels natural and easy to follow later.
Later in life, I met two other blind programmers and heard similar stories about their ability to produce well-organized code.
To bring this back to the original question: I view LSP/IDE features the same way those programmers view "visual aids." Code should be organized according to a clear and logical structure that makes it easy to navigate.
Relying on features like Ctrl+Click to find where things are located worries me. Why? Because it can mask structural flaws in the codebase. If we can't intuitively figure out where something belongs, that’s a sign the codebase lacks structure—and that should motivate us to refactor it.
Not only do I avoid using LSP features, but I’m also opposed to their use. While they can help with navigation, they may prevent developers from experiencing and addressing the underlying structural issues in their code.
LSP by itself will not prevent anything. LSP (using the terminology from OP, but any IDE really) is just a tool like any other which allows you to do things faster. It doesn't matter how organized your code base is, it will never be as fast to find some definition as hitting a keyboard shortcut (please don't use Ctrl+Click, my gosh... learn the keyboard shortcut for things you do often like this). I want to see docs for a function without moving my eyes from the code I am currently writing. I want to be able to jump back and forth between definitions without interrupting my chain of thought, see function definitions inline instead of having to jump to the file it's defined on.
When you have to manually search for files and then Ctrl+F to find functions, lookup docs in a web browser (which I hear is how people who don't use IDEs still do that), or manually run a linter/compiler to see warnings in your code, you're just being really inefficient. I can't understand that at all. Why don't you use automation to help your job when the whole point of your job is automation?
Keeping the code organized is still good advice, but has nothing to do with using an IDE.
Linting features and autocompletes are nice, also autocomplete features are nice to have.
The issue that I pointed mostly resonates with the idea that depending on LSP is the big issue.
I personally use Neovim with minimal stuff, I run lint and tests manually and not automatically, because it feels to me like push notifications taking my attention elsewhere, so I go to fix them later after what my intention is already expressed in code.
But agree and dyisagree that "Keeping the code organized is still good advice, but has nothing to do with using an IDE.". It should not be based on IDE, but an IDE on the hands of an inexperienced dev may lead to some comfort that should not be there.
I reviewed many code written by junior people, and very often I see people adding code in random places that later may let the application to be hard to follow and everything "seems fine" to them.
Other smell is like, but "it is so easy to rename a variables with my IDE" even though I believe that refactoring is nice to be done, but if we keep changing names so often, why don't we read the code that is being edited to understand well its intention and maybe realized that a big chunk of code should be split and the name choice will be completely distinct once proper refactor is made.
My point is not about avoid automation, is about having a dependency on that because the code is not manageable to people anymore, but manageable by automation mostly.
As someone who got their start in edit/Notepad, it wasn't "productivity boosts" that made me bad at code architecture when I was inexperienced.
But is that because they're following their IDE, or is it because they're junior developers? I also see that sort of issue regularly, and as someone who is very happy with their LSP creature comforts, it's still something that I have to call out and teach.
I think you're reversing cause and effect a bit here, in the sense that you're trying to argue that IDEs cause developers to be less thorough and diligent, and to care less about good code organisation. In practice I suspect it's the other way around: the people who are more likely to be diligent and get their code organisation right are the same people who would take more care in choosing exactly which tools work best for them. That is to say, it's not that IDE users are worse at code organisation, it's that the people who are worse at code organisation tend to use IDEs.
The entire purpose of computers is to automate things and reduce complexity
https://boto3.amazonaws.com/v1/documentation/api/latest/inde...
It’s the same in every supported language since it is autogenerated from service definition files. I chose the Python version because it is all on one page. Would you rather memorize thousands of functions across hundreds of classes? Should AWS “reduce complexity” by getting rid of services and functionality?
Would you rather have an auto complete IDE or have to look up the web reference?
I’m sure it’s more complex to run S3 at scale than to build out the data center I did as part of my job to store 4TB worth of data in the early 2000s.
I would much rather run a 4 line CloudFormation template and do it in less than a second over it taking months to do it in house.
I started my programming in assembly in the 80s, should we go back to that too or should we keep using compilers and interpreters?
Should we keep programming in C or should we use manages runtimes with garbage collection?
It's inefficient the first time but you quickly build a working memory and a deeper mental model of the code, which becomes even more useful in the future.
Why don't you use automation to help your job when the whole point of your job is automation?
That sort of thinking will get your job automated out of existence.
Why spend your life doing something a computer could do for you?
The goal of programming is not to write code (however much I enjoy that part), it is to solve problems.
I don't think copilot et al is anywhere close yet though. They are occasionally useful when working with popular APIs you use very rarely, or when you need to write some very repetitive code. Other than that, I feel like it's mostly a monkey typing plausible but incorrect code on my screen everywhere I go.
Why spend your life doing _anything_ a computer could do for you? Why even live, if you're going in that philosophical direction?
I don't think copilot et al is anywhere close yet though.
Unfortunately, many of those in management already think it is.
The less people use their brains, the more easily they will be replaced, and the worse the product they'll produce. Unless you want to lose your job and let society drown in mediocre software (which you will no doubt need to use), do not hand over your agency to the machine.
The latter gives me dopamine. The former gives me carpal tunnel.
Back in the 2000s for me part of that job was to lead the buildout of a data center with a SAN that took up a whole room to hold 4TBs. Now I do that with 5 lines of yaml in a CloudFormation template.
My job was not “automated out of existence” by not having to provision a data center as part of it.
But I find there to be a world of difference between every keypress being processed as I touch the key versus slightly after it.
With enough experience on a given language you just of internalize a lot of the common linter rules.
I use ctrl-space to activate autocomplete when I need it, but I find when it pops up automatically to be maddening.
I’ve been using AI autocomplete more and more, though it’s a real mixed bag between seeming magical and guessing the completely wrong thing.
Yeah, I've used xterm for so long I found it a pain a few years ago when I had to use gnome-terminal, because keypresses have enough latency to be noticeable. I never expected that from a terminal.
I spend comparably little time actually writing the code.
If there is an IDE available that works well out of the box, I'll certainly use whatever automation is available. But often it is broken, incomplete, slow, inaccurate, etc. and rather than spend countless hours fine-tuning some automation / LSP workflow that is going to break when I move to a different project anyway, I just deal with whatever features are missing.
This also has the advantage that I can quickly move to other tools, other languages, other computers, other companies, etc. without requiring days of setup and re-accommodation.
I do have a pretty good memory, which is probably a large part of why this is effective for me.
I think you are unduly harsh here. As a longtime emacs user and who switched to IDE recently (ones that come from JetBrains) my experience hasn't been what you mention. Yes there is a bit of time (not huge) to get adjusted to the shortcuts and efficiently navigate the code, but post that the IDE ecosystem is not as broken as you allude to.
Automation is not the point of programming. Think hard about this. It's the most important point. Code/build/hack to create more work for people to do, not less. It's thanatos. People need to unlearn "programming==automation" before it's too late. Everything we do as programmers should be done with "create more work" as a mantra.
The fastest definition is the one you never need to look up.
In the absence of "coding aids" like function lookup, people (are forced to) write code that is inherently better-organized and easier to conceptualize.
So to understand a project, you start at the top and work your way down, you wont encounter anything you haven't come across yet as you go.
Ironically, this 'feature' so annoyed me when I first came across the language in 2015 or so, that I put it down after five minutes and forgot about it for years.
All these IDE+- things may be nice-to-have but most soon become crutches, and then you cannot live without them, and only walk their walk, not yours.
Just turning off syntax coloring freaks recent developers.. even quite good ones.. i observed it.
Then the trend to pollute untyped-languages' codebases with so-called-"typing" noise, because "it would be IDeditor-friendly" (??) . Form over function, yes..
And another observation from many experiences.. any kind of generated code alienates its users, i.e. programmers. Yet to see how current LLM-ical trend scores in this.. As of last interview, "we need software curators, not programmers"
have fun!
There's a bit of "silly flexing" and projecting here but not entirely without merit. At regular intervals I'll actually turn all my IDE features off just to give myself a refresher. I would imagine that if you took away most IDE syntactical sugar, the vast majority of relatively competent devs could adapt in short order to it.
To me it's the equivalent of using an IME to type Chinese, but occasionally I'll just sit down and physically write the characters on pen/paper. Do I need to do it? Not really, but I enjoy how it forces me into a state of uncomfortableness.
> Then the trend to pollute untyped-languages' codebases with so-called-"typing" noise, because "it would be IDeditor-friendly". Form over function
I honestly have zero idea what you're trying to say here. You mean Typescript or using JSDocs to inform type expectations? What exactly is the issue?
These kinds of arguments get "trot out" all the bloody time. Whatever I grew up with was the most ideal - whatever came after is just a crutch.
For that matter, why are you using a high-level language like Javascript or C#? Don't you know that it's a crutch and gets in the way of true programming understanding? You should be using magnetized needles to etch machine code directly onto disk platters.
Yeah, this is going to be extremely controversial: your so-called untyped language actually has types in, and they're important, but only the program can see them at runtime because you've not written them down anywhere.
The real solution to "repeating myself writing down types" is Hindley-Milner inference, which dates back to 1958, and more languages should use it!
... and?
Cars have automatic chokes, power assisted steering, hydraulic brakes, airbags, seatbelts, auto-dimming mirrors, self-running wipers, and it's fine. People argue that driving a stick-shift is more authentic but even they use syncromesh and clutches, they don't complain about how rubber tyres are for wimps and they use cartwheel-style metal bands wrapped around wood. They don't complain about having a differential instead of fixed drive to the axel.
A plain editor relies on terminals displaying multiple lines and updating them, filesystems for storage, an OS, a keyboard, ASCII or equivalent, maybe virtual memory management and TUIs and "then you can't live without them"! (scream emoji). But so what, it's not a problem, nobody seriously programs on punched cards because they don't want to become dependent on screens and keyboards.
You can make a case that artisan craftsman furniture is better quality than Ikea furniture but that's not because the artisan avoids factory machinery and uses hand tools, it's that the artisan spends more time and effort and takes longer (and needs to charge a lot more money). If you want to make the case that the artisan shouldn't use tape-measures, laser levels, power sanders, power saws, dust masks, wood glue, clamps, they should use a pencil behind the ear, a thumb's width and a handsaw because that's how they learn the True Nature of Wood(tm) then maybe you're just posturing and gatekeeping, or have some sort of Amish-style religion.
Calling power tools 'crutches' is so you can imply that people who use them have disabled themselves. Instead it's like saying people who commute 20 miles to work in a car are using the car as a crutch because you can just walk 5 hours each way and that's more real. It's embarassing that people are so desperately trying to claim both "I don't use power tools because power tools are for weak babies and I'm a STRONG MAN" and in the same breath "my editor can do everything an IDE can do, it isn't inferior, it is a power tool!".
> "Just turning off syntax coloring freaks recent developers.. even quite good ones.."
Just turning off thermometer and oven temperature controls freaks recent cooks ... even quite good ones. Are you going to make the case that restaurants would be better if all cooks worked like your grandma with a log fire who had learned the right feel of heat for bread, cake, or roasts? No? Because that's obviously silly? But what if those developers one day in 2025 find themselves on a monochrome screen, with no shades of gray and no underlines or italics and no option to buy an alternative, hah what then?! Isn't that obviously also silly? NB. you didn't remove paragraphs or punctuation or capital letters from your comment, maybe you're just not a good writer and need these assistive crutches?
Or you're William Morris, who was kind of ridiculous and pretentious, but was making a valid point at the same time.
https://en.wikipedia.org/wiki/Arts_and_Crafts_movement
In your examples white on smoke white text would be the better analogy
We rarely talk about it and it’s also the constant source of teamwork issues. Look, if our code needs structure to be observable, it lacks structure in the first place.
Languages and editing methods — not talking IDEs here, it’s more fundamental — are still “empty file canvas, paint anything” model. That must be stopped.
These structural issues are analogous to malformed xml issues. They should not exist normally. We could start with e.g. better isolation/visibility systems than “files with exports”, then address lack of annotated TOCs for projects and modules. Then replace files with something more database-like. Theeen we could talk about someone breaking a nice structure. Because this structure only exist in a human’s mind, blindness only serving as a bitter “equalizer” to it.
I do not rejoice with code bases where every file has no logic or code in it but there are hundreds of methods and files with everything spread out. I have no idea how those projects fit together because the actual logic is spread out.
For my personal side project hobby work it's all in one file.
There are two kind of mazes:
- those that are so tangled up that you can't make out any kind of structure.
- those that are so regular that you can't make out any kind of structure.
Not because it is split it means that was well split.
If you can reason where something should be, it may be badly organized.
Even single file projects can be organized.
If is small enough and makes sense, why not?
But even in a small file, how is it organized? Utility functions maybe at bottom so you keep business logic together and visible? Maybe some other logic to make it cleaner? The way functions are named follow some convention? What about variables?
I once read one opinion that software developers had more in common to artists and writers than engineers. Writing code is like an art of expressing yourself well to your peers in a way that a computer will also execute what you meant, not the opposite.
The computer can understand whatever entangled mess you write, other people (which may include your future self) may not.
They could also link to other word clouds
Generated and updated automatically
Ok, so this is work, but:
Not many editors are comfortable with a million line document that's 47MB. And that doesn't include generated code (which I very rarely need to look at, but is right there under F12 if I do)I've been thinking about it lately but what I've seen in IDEs is not what I want
I liked OpenGrok
You should organize your code similarly and with as little fanfare. They are just tools, dont get clever with them. Sometimes, you get too many forks in same holder so you have to divide them but again, common sense prevails.
I work with someone that navigates the tree structure of all our directories every time they need to look for something. It is painfully slow to watch and if you ask me, they produce fairly spaghetti architecture. Some of that could be a lack of familiarity with the idioms of our platform and its available SDKs.
But I ⌘ click all the time as well as quick-open right to the class, variable or function I need to work with, and I feel like I still organize things better. I’m constantly fixing the weirdness and proposing even more things that should be improved.
You might even get a bit of a Conway’s Law effect by committing unnecessarily to a specific, onerous work style.
Counter example: I’m working with LSP dragging codebase where basic code lint takes 45s. My colleague takes a break after each change so that their code navigation starts working again.
Inefficient people are inefficient.
Not using LSPs isn’t guarantee of efficiency just as using LSP isn’t one. Different strokes for different folks - one has to accept consequences though (more automation means less presence, but it’s everyone’s own choice to make).
When I met the other 2 blind people, one of them joked that yes, they were very organized, but because they had to be, unlike us, being not organized wasn't an option for them.
I don't question the ability you may have to deliver quicker results, but I bet that your incentive to organize stuff is smaller than what your friend has.
Maybe he will be more inclined to refactor that than you and take even more time, and that maybe it pays off. Of course, I have no idea of your deadlines or if you are in a rush for MVP in search of market fit.
But this also follows those lines of: "wanna go fast go alone, wanna go further go together".
I value more quality than speed, but also understand that sometimes speed is what is needed.
I've been doing work on a Raspberry Pi project, which got large enough that it took forever to get code nav back after an edit.
I switched to using VSCODE remote from a beefier machine with 32GB of memory (and an admittedly better processor). Editing response is now under 10 seconds. Sweetness and light! And even the compiles (which still take place on the Pi) run about 6x faster.
When people say "just organize your code well", this isn't something that happens perfectly on the first time, it's something that evolves as the codebase grows. IDE features reduce the barrier to reorganizing as we better understand the problem domain or as the solution grows.
My experience is that those who don't take advantage of their tools produce worse output, even if they believe they are producing better output, the organization they've created only works in their mind and not for their teammates or other people who later inherit the project.
from the parent (i am not the parent)
> I try to follow the logic and structure of the project in a way that feels natural and easy to follow later.
in my own experience it applies to everything in a codebase.
what the end state looks like completely depends on the problem domain at hand, and the tools used to solve the problem.
how to make a codebase “intuitive” is learned, not taught. understand the problem domain, understand the tools in use, then refactor, refactor, refactor, refactor until it makes sense.
> I work with someone that navigates the tree structure of all our directories every time they need to look for something
sounds like they have yet to learn how the code modules are organised.
possibly because the modules are not intuitively organised, possibly because there’s a lot of code, possibly because they just don’t care and don’t mind the mental break of mindlessly looking for the right module.
When reviewing code, the reviewer responsability is not only to make sure that it works, otherwise tests and its coverage would be enough, but they aren't.
If a reviewer sees that something does not fit with the convention that the team agreed, it should be questioned and also teached or discussed (if some new addition force the team to do so).
You don't need to know all the code base by heart, but how to follow it and place it.
Something like, does your team uses DDD? Where code that interacts with database should be? And how? How your templates are done? Do you use presenters? When? How? Any other patterns?
It could be applying DDD or some other pattern with that makes sense to the team.
If you are using a framework like Rails, learn well its conventions, follow them and change what your team likes and made it clear to all team members.
Also learn well SOLID principles and OO (of functional programming, or whatever you are using) and make it obvious that your code fits well your choices.
In other words, if some file (class, template, whatever) should have a single responsability, and by that, it should be obvious where that will be placed.
It has more than one responsability? How you can split it? And once it is splitted, it should be obvious where it should belong as well.
I don't find it onerous at all. I consider it the same kind of responsibility as writing tests and documentation. And I certainly don't think Conway's Law applies, since for my own projects I'm generally creating deep hierarchical call graphs while being the only person in the "organization".
Everything is “logically organized” am I suppose to remember every method on every class?
The C#, Java, Go and other SDKs are the same (they are all auto generated by the same definition file)
It doesn’t matter how well structured a codebase is, once it gets large, you’re not going to remember every method on every class
1. The HLSP user
This group does use an LSP, it turns out, only via you as the conduit: you are their Human Language Server Protocol. They'll ask, ‘can we pair today?’. From that point, navigating code, looking up method names and signatures, and flagging syntax errors before runtime or compile time is your job. How they find their way around when you're not there is unclear, but it's likely slower.
2. I am one with the machine
This crowd has been wrangling code long before IDEs, LSPs and LLMs. They've amassed a terrifying arsenal of tools to find files and references, bulk-rename, pull up docs, and twiddle bits in ways that looks fluent and natural enough when _they_ do it but would take months or years for you to learn if you didn't give up and go back to your IDE. When you ask them _how_ they just did something, they either pull up a short page of Elisp and say, "it's really quite simple we just reverse the node tree and iterate over the folded quarkenspace", or — if they're a vim user — say something like, "oh, that's just the hiboscus plugin, but piped through smark".
So the response to "how do you do it?" seems to be broadly summed up as, "the way we've always done it".
Group 2 isn't necessarily _better_ than LSP/autocomplete/heavy LLM users (people who are very proficient with more modern tools can look like magicians too). The LLM-free crowd were just forced early to become proficient, they've gotten very good with their home-rolled methods, and — in many cases — they probably wouldn't benefit that much from LSPs, or they use them but with features that bug them switched off.
Group 1 would probably better be served by an LSP but either don't believe in the benefits or is just comfortable with their current setup and feels no pressure to change.
My answer to the fairly common question of "how do you do it" is always the same though.
I always relay that it's taken an evolving 20 year career to learn all that voodoo, I never imply it's simple to gain that knowledge nor do I encourage others to change their working patterns to match mine. I do like showing off a few tricks though!
One thing I do always mention though is the longevity of vim + the command line. I went through a period of swapping IDEs like most junior devs, now I have a setup that I feel can last me till retirement and I can just continually invest in learning to use the same tools better
Oh man, it makes my blood boil when someone asks in which file the function lives, and I know they have a "go to symbol" in their editor of choice, but somehow they never want to learn some features of their tools. I did notice that how you use your tools changes how the project is structured. Neatly organized files and directories are a must if you are not comfortable with quickly jumping to a symbol.
If anything, code-aware tools let you learn the structure of the whole program quicker because you can quickly drill into and explore any arbitrary part of your program (and libraries)
A better summary is: “people have different workflows because different people like to approach problems differently.”
That’s all it is.
I find LSPs largely get in my way. I don’t have them disabled because 1% of the time I do find them useful. But for the other 99% of the time they’re adding no value at all. So I could very easily write code without an LSP (and often do write code in vi with zero additional plugins installed).
What I do really value is code formatters. I like not having to write tidy code and then having code formatters clean it up when I hit save. That’s the kind of enhancement I value. (It also gives me piece of mind that a file has saved successfully when you see alignment pop into place).
But the way you like to approach problems is heavily influenced by your history of solving them, what tools were available at the time, what you chose to adopt, and how often you revise that toolset.
It's not elitist to say that people tend to use what they've always used, which was my point: "how do people cope without LSP/LLMs?" tends to be "the way they've always coped" — efficiently or otherwise.
And it tends to track with when you began programming or using specific tools. A vim user who adopted vim in the '90s is more likely to have a light config than those who adopted it more recently, where I see heavy plugin and plugin manager use, LSP integration, pre-configured distros, and Lua-based configs.
Generally I find IDEs more useful when I’m working with an unfamiliar code base or an unfamiliar language.
And that’s probably why there appears to be an age gap. It might be more down to comfort with the code rather than comfort with the tooling.
You’ll learn where all the reference docs live for your language, libraries, and frameworks, and along the way you’ll learn more by actually reading the docs.
You’ll learn the value of good project organization and file naming, and explicit import statements.
And you’ll learn that the speed of typing, even long method names, is not the limiting factor of your productivity.
> You’ll learn where all the reference docs live for your language, libraries, and frameworks, and along the way you’ll learn more by actually reading the docs.
I know where they are. I use them every day. But reaching for them to check every argument if every function I use is a waste of time. It als only works for stuff that has a full suite of documentation. If you’re using an internal library you’re very likely searching the code anyway.
> good project organisation, file naming and explicit import statements.
Good file naming is a crutch for a lack of tooling to explore and modify at a project level. Good project organisation doesn’t require you to disable tooling with semantic knowledge of your project. And explicit imports are a (necessary) distraction m.
> And you’ll learn that the speed of typing, even long method names, is not the limiting factor of your productivity.
It has nothing to do with the speed of typing and it never has. It has everything to do with working at the abstraction level I want to work at - my codebase. It’s using the tooling that means I think about “I have an X and I need it to do Y. “ rather than “let’s go context switch to the browser to figure out if it’s len, count, size, chars”
A good corollary is a linter - it’s incredibly important to the project, knowing and understanding the rules and why they exist is very helpful and would benefit everyone on the team. But a computer can do it for us, and let us focus on writing the code rather than ensuring we have tabs instead of spaces.
In the past when I've tried to use IDEs, I've often given up within minutes because they would keep doing things that disrupt my flow - like popping up an entire menu of autocomplete suggestions in a way that obscures other code I'm trying to read, or autotyping a close bracket that I would have typed anyway by muscle memory, or not making it clear how to move the cursor past something it auto-typed, or interrupting me with warnings that something won't compile when I haven't finished typing it....
All the things that can be turned off/tuned. Strange that you don't spend the time learning the tool you admonish others for using
There are times when the "programming" job is best done with vim or even just a notebook in a quiet place.
Other times, an LLM or IDE can help get a new project going faster. Especially if you are unfamiliar with the language or framework. But in the latter case, do review the output until you understand it. So far even the best LLM will make subtle bugs, but unless its in a language you know well, you might have a hard time spotting them!
I’ve replaced so many tiny manual processes with python and batch scripts, and rewritten them in go when speed became an issue because of Claude and ChatGPT. I know enough to spot the glaring issue. I’m never going to sit down and write a batch file that installs my toolchain as it’s not done enough to warrant my time. But Claude will, and now our build machines use the same scripts and it means everyone is in the same environment (c++ on windows so our options are limited here)
> You’ll learn where all the reference docs live for your language, libraries, and frameworks, and along the way you’ll learn more by actually reading the docs.
With neovim/LSP, I have a key binding (Kg) that opens a small window within the editor with the rustdocs. Not only this is faster than going to the browser, typing the type name, opening the docs, looking up the method but also it ensures that I am looking at the docs of the correct version that my code base is using.
I don't really understand the aversion to using these tools. They are "auto-complete" and not generative complete. This means you need to know what you are going to type for them to help you.
No one is yucking your yum, it's not an aversion, it's that we don't need them, it never really occurs to us to use them, just like it never occurs to you to not use them. You make it sound like people who don't use them are fumbling and tripping over themselves and aren't actually writing any software.
This doesn't really make sense, either. How do you not consider the right tool for the job? Do you just use whatever you learned to code and never really bothered to consider that you might be more or less productive with different tools? LSPs don't always offer a productivity increase in all contexts—they seem to be mostly useless for dynamic languages like ruby and lisp and javascript—but I have a hard time imagining a programmer that couldn't benefit from them in the right context.
And they're just a straight-up improvement from iterative compilation and ctags, the latter of which never really worked very well in the first place.
I don't have a problem with my "productivity", whatever that means. I'm not trying to write code faster, my own experience is that writing code faster leads to shitter code. I'm not hindered by my tools, and most of my programming time is spent thinking and understanding, and none of these tools have afforded me better thinking or understanding, and often times they get in the way of my thinking.
I'm wondering who these programmers are who are sitting down for eight hours a day, pumping out perfect code at 200wpm without any breaks. I suspect they might not exist.
You should give these tools a try. From my perspective it's like someone sitting on a small wooden chair and you are proposing an Aeron. Yes, they are both chairs and you'll be sitting and perform the same task but boy the ergonomic chair will save you from some real back-pain.
Meanwhile some of us already had setups broader than that in scope: I use vim, a tiled window manager with vim-like keybindings, and Tridactyl to control Firefox with vim-like keybindings. Can do just about everything without touching the mouse, including browsing HN and other sites.
Plus, I have more time and energy for writing code later. Or other activities.
I admire consummate professionals who can immerse themselves in the craft. I can't do it. I still put out great work - definitely not the best ever - and the tools lessen my mental and physical burden. I have more energy that I can spend elsewhere.
We replaced the punchcards and terminals and line editors with different tools because the trade-offs were worth it. Maybe we can keep adapt new tooling in a similar matter when it makes sense.
every line of code is a liability. every line of code needs to be thought about. every line of code is precious.
spamming autocomplete means i’m not thinking about what i’m doing, even the simple stuff like writing a for loop statement.
> every line of code is a liability. every line of code needs to be thought about. every line of code is precious.
I personally think this is ridiculous. It's code. It's just formal instructions. You process them and move on. It's not describing the face of god. If you need to think so much write less clever code.
The right kind of clever results in "oh, it can be that simple?", not "what the hell is that line noise?"
the point i was trying to convey with the phrases was my ideal attitude when writing code.
this code matters. lets think about it, get it done right and not throw it over the fence for the poor schmuck who comes along in two years time after we’ve all left.
i’ve been that schmuck. it was horrible. i don’t want to put other people through that. ever. (this is now my stuff, but hopefully explains some of the attitude).
our attitudes, intentions and motivations count when it comes to code quality. look after your code and it will look after you.
that’s what i feel. you feel different? that’s fine.
I'm just very confused how the conversation took a turn away from pragmatism and towards the aesthetic.
> I personally think this is ridiculous. It's code. It's just formal instructions. You process them and move on. It's not describing the face of god. If you need to think so much write less clever code.
you called my attitudes to writing code ridiculous. i thought i’d provide you some information on why i hold that attitude. maybe it might help you understand. maybe you may consider your own attitudes as a result.
it seems to me that you are not ready yet.
The thing people keep trying to explain is that when it comes to this stuff, there isn't a "right tool" for every job and every programmer. You like LSPs? Great, use 'em. You don't like them, and you can be just as productive without them? Great, turn 'em off.
So many people yelling at each other over personal preferences. It's not enough to have the tool and use it, you need everyone else to use it too? Why? To validate your own use of the tool? If you really truly believe it gives you an edge, great. I don't think it gives me one, and anyway, not everyone fights with the blade.
If your code editor looks up the doc, you find yourself reading what the code editor shows you.
Wikipedia, (real) historians have some aversion to using it. No argument, it is convenient.
Whereas if your editor takes you directly to the method in question, you miss that opportunity.
We have tooling for offline docs of course. See dash (macOS) or zeal (win/linux)
I can use a convenient shortcut to go as deep as I want in the codebase, including libraries. Meanwhile crowd with clinical case of tool aversion will spend time fuzzy-searching and manually sifting through text.
Who's encouraged to explore more?
Autocomplete has never been about the speed of typing things, and always about finding and recalling abstractions that you sort of know but haven't committed to strong memory yet.
This is the fundamental truth that those pushing AI as a replacement for programmers miss (intentionally or not).
I still don't understand how it would hurt if you can focus on the parts that do influence your productivity and have the rest appear automatically. But that's just me.
I think this blend of comment shows a good dose of ignorance discussing the role of AI as a replacement for programmers.
It's not like PMs are suddenly seeing engineers vanish from software projects. It's that AI makes developers so much more productive that you only need a subset of them to meet your work requirements.
To give you an example, AI tools can indeed help you write whole modules. Yes, code can be buggy. Yet, the "typing" part is not what developers benefit from AI. Developers can iterate way faster on designs and implementations by prompting LLMs to generate new components based on new rewuirements, which saves you the job of refactoring any code. LLMs can instantly review changes and suggest ways to improve it, which would require either reading up on the topic or asking a fellow engineer on payroll to spend their time doing the same job. LLMs can explain entire codebases to you without asking a single question to veteran engineers in the team. LLMs can even write all your unit tests and rewrite them again and again as you see fit. LLMs can even recommend best practices, explain tradeoffs of approaches, and suggest suitable names for methods/variables based on specific criterias.
This means AI can do a multitude of jobs that previously you needed a whole team to do, and for that you no longer need whole teams to do a job.
If we train ourselves out of being able to do these tasks, won't we find it harder to recognise when the AI makes mistakes?
I wasnt aware of any that could
my experience so far is that it takes away the upfront thinking and leads to a mess of code that I then have to sit down with them and try to work through.
1. I know where the reference docs live already. Auto-complete doesn't obviate that need.
2. Again auto-complete doesn't completely obviate the need to navigate by file name/path (though it does reduce it), so I already know that is valuable.
3. The speed of typing sometimes is a limitation. I'm not a slow typer but sometimes there are situations where you just have to type out something tedious, and you're not doing anything other than typing. Yes even with fancy Vim/multiple cursor editing. Auto-complete speeds that up a little, but Copilot sometimes speeds it up a lot. You should try it.
For example for printf debugging, with copilot you can pretty much just type `dbg!` and hit tab. Much faster.
You also missed some significant advantages:
1. go-to-definition makes navigation much faster
2. type inlays make understanding the code much faster
3. auto-complete makes looking up method names much faster (is it .starts_with or .has_prefix? .contains, .has, .any, or .some?)
The only upside of not using LSP tooling is that sometimes it can bring your computer to it's knees, whereas plain text editing will always be fast. Easily worth it though.
Both of those languages are strongly typed.
It seems you're confusing the fact that the type checking happens at runtime (that is to say, that they are dynamically typed) to mean that they're "untyped", but that's just not the case.
If one is confused about the languages one is using at this level, then modern tooling features aren't likely to help much.
Then there's dynamic and weakly typed, ie. JavaScript. Dynamic and strongly typed, Ruby. Static and weakly typed, C. Static and strongly typed, Rust.
Really great for quickly showing you errors and providing passive feedback. I also autocorrect linter errors on save.
But everything else about how I do work is basically unchanged; I haven't found autocomplete to be worth it.
I think meeting developers like this will make you dislike autocomplete. If you know what you're doing though, it is amazingly helpful and can save you many wasted hours on silly syntax errors.
I rely less on documentation because I've got it all memorized due to not relying on autocomplete.
This is how I personally use it for discovery, anyway. The other day I was writing some Rust code and needed to remove a prefix from a &str. I tried a few common names after ‘.’ to see what would autocomplete, before finding that Rust calls this idea “trim_start_matches”. I then wanted to know what happens if the prefix wasn’t present, so I just hovered my mouse over it to read. Now, if I was writing Rust a lot, I would end up memorizing this anyway. I’ve never not written Python without a similar tool involved, yet I have a pretty close to encyclopedic knowledge of the standard library.
I feel similarly about go-to-definition. I often use it the first time I’m exploring code, or when I am debugging through some call stack, but I also always do read where I actually end up, and do form a mental map of the codebase. I’m not sure I buy either the contention in this thread that these “crutches” make developers uniformly worse, or that removing them would make all poor developers suddenly more disciplined
The classic example of getting this wrong is probably C# developers using IEnumerable when they should’ve used IQueryable.
Or literally any function from the standard library in C++, which will likely have undefined behaviour if you look at it wrong and didn't read the docs.
The closest thing coming to mind to me is mixing up IEnumerable and IEnumerator when trying to define a coroutine.
Not every C# developer knows the difference, in my region of the world it’s an infamous mistake and is often the first thing you look for when tasked with improving bottlenecks in C# backends. On the flip-side, using IQuerable isn’t very efficient if what you wanted was to work on in memory collections.
There is an equally infamous somewhat related issue in Python with list vs generators. Especially common when non Python developers work with Python.
Thank you for the explanation.
If keeping my job requires relying on the latest tech and tooling, that that’s what I’m going to do.
I do all of these, I also use auto complete, all LSP features and then some. And ofc copilot etc too.
Not using productivity tools is just hurting you. I don't know what sort of misplaced sensation of superiority doing things the hard way may bring to you, but please don't give bad advice like this to young people. If they want to experience writing code without the help of modern tools, they can just use one of the so many languages whose tooling sucks: Nim, D, Odin, Zig come to mind (not criticizing them, but they still have a lot of work to do, and I know that they're all investing on doing it because yeah, everyone wants it). Nearly every single developer who uses those tools complain about tooling if they have used better tooling before (e.g. Rust, Dart, Java, Kotlin, C#).
When I have top-notch tools, I can write code that's a lot more advanced than when I don't (and I know that too well because I still insist on using those languages with bad tooling, unfortunately for me as it's a pain sometimes), because everything becomes much, much easier: docs, navigation, inline warnings, auto-suggestions (for languages that you're not too familiar with that's a godsend, love IntelliJ for Java, Clippy for Rust, for example). You actually learn from those things (as opposed to continue learning things that don't matter). Don't be a software ludite.
This is a muddled hyperbolization. These features barely existed (in terms of widespread use) even 10 years ago, but everyone got along just fine, even founded FAANG-scale companies without them, and so on. Yet you're making it sound like if you aren't using them, then you're basically still writing COBOL.
Not using productivity tools is just hurting you.
The commenter very clearly isn't saying you shouldn't use them. And their point had nothing to do with any "sensation of superiority".
They simply meant that -- yes, it is possible to not only be productive, but highly productive without these tools -- and that if you just tried to get along with them for a while, you just might learn a thing or two. For example, if one is even the slightest bit hesitant as to how one would go about resolving one of the issues that the OP asked about, without the help of modern tooling features:
Then one definitely has some important learning to do.It's a great language but I feel like it requires some tooling
Personally, my recent work has been: short term freelance work, new code bases, a variety of programming languages including ones I haven't previously used, problem identification and solving, and mature code bases where you'd only write a couple lines of code a day.
In that context an IDE optimizes a tiny part of the work, while getting in the way for everything else.
I'll add that when I worked on a greenfield Java project as part of a large team, I absolutely used an IDE (IntelliJ at the time) and couldn't imagine working differently.
https://github.com/facebook/PathPicker
I don’t think VSC is a very good IDE though. I have no idea why a Vim user would use it instead of upgrading to Neovim, and obviously doom emacs is the best choice but I do think the terminal is actually pretty good in VSC. At least on Windows.
As someone who has only used inferior IDEs to VSCode (grimaces at the thought of Xcode) and who thinks that VSCode is very good, what exactly am I missing from better IDEs?
That being said, use what works for you.
Or Cmd+Click/Ctrl+Click in an IDE
On unfamiliar codebases a mouse with back and forward buttons can be quite a fast and convenient way to get the "lie of the land".
I often do this inside GitHub's browser-based editor.
Yes, using the vimium extension. My only problem is text inputs like this one, my muscle memory expects vim keys to work and I type a lot of jibberish that I have to clean up. I know there are extensions for that too but I can live with it.
(I use a Superior Technologies trackball because it's the only mouse I could find manufactured in the USA and it's really not very precise, it's kind of a self handicap to nudge me towards keyboard shortcuts, muscle memory gains just have a steeper curve on keyboard than mouse)
And find/grep are very bad and limited tools for this compared to what an IDE will give you
And rg also works in projects with multiple programming languages (e.g. you're using a method from JS and want to find its Rust implementation in the webassembly module) and across project folders (instead of pointing you to a read-only copy of an installed module). But, if you spend 95% of your time in a single codebase with a single language... YMMV.
Yes, when searching across the entire project for text an instant fuzzy search is an invaluable tool.
> It will also show when a class with the same method names exists elsewhere
Oh, this is a great example when a code-aware tool is better. I also ave a rant-ish about LSP at the end.
So, let's say you have several methods called `update` across your codebase. As per original comment, "I rarely need to find only the definition of a function, but rather other call sites too".
With text-search you will find all those other methods, and all the call sites for those methods. And not just in the working code, but across all tests as well. So you have to manually go through the search results and figure out whether a call to update is the one you're looking for.
In an IDE it's just a shortcut:
- go to definition https://www.jetbrains.com/help/idea/navigating-through-the-s...
- go to implementation https://www.jetbrains.com/help/idea/navigating-through-the-s...
- Find usages https://www.jetbrains.com/help/idea/find-highlight-usages.ht... perhaps even inline https://www.jetbrains.com/help/idea/find-highlight-usages.ht...
- find the full caller hierarchy https://www.jetbrains.com/help/idea/viewing-structure-and-hi...
And so on.
Oh, you want to know where all the methods called `updated` are defined to go and scream at someone?
- Search for symbols https://www.jetbrains.com/help/idea/searching-everywhere.htm...
Of course all that is also integrated with things like refactoring. So if you rename a method, you don't have to painstakingly manually search for all invocations of that method. Or if you rename a parameter to that method, or... See the several dozen possibilities here: https://www.jetbrains.com/help/idea/refactoring-source-code....
----
Here comes the rant.
The whole "text searching is enough, the compiler will pint to me the errors I've made" fashion has held back tool development by several decades. It's telling that LSP (which has barely 5-10% of IDEA's functionality) has taken this world by storm. Suddenly the languages and the editors that lacked even the basic quality of life improvements when working with code got a glimpse of what is possible.
Welcome to the very early beginning of the 21st century. Perhaps in 20 more years you will finally let go of the notion that painstakingly doing a computer's job is not in any way or form productive or indicative of a great programmer somehow [1]. And that to work with code you really want tools that are capable of working with code.
After all, somehow, you reach for Excel/Numbers/Google Sheets to work with spreadsheets, for Photoshop/numerous alternatives to edit photos etc. You don't reach for some generic vaguely-adjacent tool to do the job.
[1] I keep telling this story: About 5 years back I saw several people switch from vi/emacs to PHPStorm/IDEA after watching me zipping through code (including jumping from Symfony configs to code and back) while they spent often minutes doing fuzzy searching through the codebase we were working at the time: a huge legacy monolith we were slowly refactoring and splitting up.
I also don’t think it’s a coincidence most people in the comments seem to be using some flavor of vim. I think using an editor designed before these tools were available will make it much easier than afterwards.
I’m not sure I’m the best case, but I just turned off copilot, haven’t bothered to configure my autocomplete yet in my latest neovim config, and only use an LSP so my macros can be highlighted differently. I write primarily in Clojure though, so inline docs and go to definition are just a part of life.
I don’t really have anything against LSPs, but neovim, telescope, and the repl have always gotten me where I want to go
With regards to my flow, I’ll normally have 1 vim session for every 1 project, each with 1-5 tabs open, each tab with up to 12 splits. There isn’t really structure to the tabs or splits, I’m just liberal with opening splits and conservative with closing them. To me it can be really helpful to have all the recent code I’ve been working on open at one time, and if it’s not, I just pop a new tab and start fresh
I find people who drive with sat nav often don't learn almost anything about their route (they don't have to! is the whole point!)
I at least orient my navigation north side up so I have a basic clue what I'm doing.
A friend of mine looks at navigation before driving, then turns it off.
I organize notes and action items in a paper notebook in front of me or a text file in another editor pane. I read the code. I search and diff with standard POSIX utilities that are available in every environment. In the languages which support it, I try ideas and answer my own questions in a REPL.
Practice leaning on tooling all day and living without it will feel unthinkable. Practice doing without, and you'll find you need very little.
I'm not saying it was the good old days but it was not a significant barrier to doing what we wanted to do. Code is code and ultimately you need to have a clear mental picture of the codebase, regardless of your tools. LSP isn't valuable because it allows novel functionality (there are dozens of different techniques for discovery within a codebase). LSP is valuable because you have an efficient way to "query" your codebase for contextual information directly in the editor window.
But you can build that knowledge through other means, you just need another terminal window and external tools.
The understanding of the codebase is what maters. LSP is a means to that end, making an already common task slightly more efficient.
> look up the definition/implementation of some function
Text is effective. git grep "def foo"
Autocomplete engines work on text too, based on the contexts of the buffer or files in the directory.
Static analyzers have always worked wonders in identifying LSP-like warnings.
Compilers and debuggers give you access to state and type information.
This stuff has been around for decades. LSP just makes it more convenient and packages it into one UI.
For what it's worth, I'm 100% in on LSPs for my work these days. But if LSPs disappeared tomorrow, I could revert to the old ways with only a small hit to productivity.
In code I have control over myself I avoid imports that doesn't enumerate all imported symbols. That is I always use the import Library (symbol) syntax in Haskell and never do wildcard import in Python.
When coding C I sometimes use tags so that I can go to definitions quickly, I should probably use it more than I do to be honest.
I do use hippie-expand for quick auto-completion but it is completely textual, it has no understanding of the programming language it currently works on which makes it much less powerful of course but it also has some benefits.
I always have the documentation very reachable, I use a search keyword in my browser to search on hoogle. I type H <Space> symbol/type-expression <Enter> and I quickly find the documentation.
I do use Visual Studio on a Windows box for working on a C# codebase a couple of times per year and when I do I always turn off that code lens thing and I find that I rely on the code navigation features quite a bit. Part of it is probably due to it being a code base that is larger and that C#(/Java)-flavoured OOP makes the code more spread out. In terser languages like Haskell (which is much terser) it is natural for more functionality or types to live in the same file which means that you get much further with just simple textual search.
Then I started working with Scala and, until metals (the language server for Scala) and LSP support was good enough (first vim, now I'm a happy nvim user), it was awful.
So I'm certain it depends on the language. My take before Scala clicked for me was that I didn't want to use a language that required an IDE (or IDE-alike features) to be productive. And I think that opinion was mostly because my bad experience with Java.
I still write C without LSP, and I'm fine.
I'm wondering how many people in the comments would misinterpret it as "Emacs is outdated"/"Emacs does not have modern LSP-based tools"
Recently though I couldn't resist experimenting with Copilot and I switched to VS code for it, after 32 years. Is there a good Emacs module for it by now?
Moving beyond that, it isn't like LSP originated the "go-to-definition" feature. Indeed, this is one of the things many of us like about Emacs. You can literally jump to the definition of most any function currently in use by the environment. Can even edit it and have that edit immediately open.
But, moving back to my original line. The answer is people work in different ways. Is why some folks get really good at typing on a keyboard. Most people probably are not fast typists. But that doesn't necessarily mean the advantage a fast typists has will be realized in any actual gains.
I don't use IDEs either. A plaintext editor and a console window (for compiling, testing, and the occasional grepping) is enough. IMHO someone who is reliant on IDEs and autocomplete is in the same category as not knowing how to type without looking at the keys on the keyboard --- yes, you can get by, but you will never be able to code as fluently and effortlessly as those who can think deeply about the code entirely in their brain, and you'll be frequently writing code that looks reasonable when viewed in isolation but obviously redundant and/or inefficient if considered as part of the bigger whole.
I've worked on projects where "hold the entire project in your head" might be somewhere between very hard and impossible. Work the right abstractions, it should be possible to hold the relevant parts in your brain. Still, when you're new to a large project, an ide goes a long way.
That's an example of one where a large number of people working on it do not use IDEs.
I've worked on forks for certain Android devices with very primitive text suggestions that notepadqq provides and the difference in productivity between that and vscode+clangd is staggering. What do you mean there's a macro for anything?
The intern was tasked with a large-scale refactoring of some modules which the senior devs estimated would take months of work to complete. After shadowing a senior dev to get an idea of the work involved, they realized the dev was literally string replacing a lot of the code, hence why the estimate was ridiculously long. The intern instead loaded the project into IntelliJ and used the built-in refactoring tools to get the task done in a couple days. This caused a bit of an internal shit-storm with the product owners because the intern made the devs "look bad".
When it comes to autocomplete specifically I initially turned it off because I was mainly a C++ programmer and C++ autocomplete has just never been good enough and a half-working autocomplete is just worse than nothing at all because you end up stalling and waiting for the prompt. But eventually I just grew to hate it for all languages because it interrupts flow and "pipelining".
This was also my first reaction when I saw someone else using an IDE. My way of writing code is to get my ideas written into the editor first. Only after I have written the code, I run the compiler to see where things don't fit. The people who were using the IDEs were really astonished that I could understand the output from the compiler on the command line.
Programming is a lot more fun when you sketch out your ideas first before you make sure that all the details are correct.
There are tools like ctags that have been around forever and build an index of definitions to let you jump to them, but personally I've never found a use for it, anymore than I ever found a utility for IDEs. I just have multiple terminal windows (or tabs) open along with my editor (emacs).
When I'm deep into a project - even a very large one, I basically just remember where everything is (as well as the code itself), or when needed just use terminal find/grep. For stuff I've written from scratch myself (vs at work working on a large legacy codebase), code organization is certainly part of it - organizing code into file-based modules. I use record/playback editor macros a lot to avoid typing, and occasionally use sed (scriptable Linux command line editor) to perform entire codebase renames etc.
I've tried modern IDEs like VS Code, but really don't find them to be a productivity benefit. I get the appeal of a single tool/environment that does it all (like old-school hard-core emacs folks who treat it as an IDE), but in terms of productivity you can work just as fast with a collection of tools rather than a single one.
Yes, I try to learn the language well enough that I know the parameters of common functions. If I don't, I consult the docs. If I need to find the definition of a function in another file, I use grep.
Disclaimer: I do use LSPs extensively, but they do what they are intended to do when I explicitly ask. Offer completions. Format code. Organize imports. All that is available, but only happens when I ask. Same happens where there's no LSP support, and basically the same commands depend on ripgrep, fzf, semantic-grep, external linters and formatters, and plainly compilers.
Me and my tools take turns. It's almost 2025, practically everybody agrees that concurrent mutation of state is a source if trouble.
However, I still relish in the ability to open up a nerfed Sublime or basic Vim install where it’s just me and the text. I do so when I want particular lucidity in my thought process and code composition.
Then I can always fire up heavier tools for code review after the fact. How do I “do” it? I got really good at grep’n my way through a code base. It’s amazing what grep/ack/rg/etc can tell you when you know what to ask. And if you don’t know what to ask, well finding that answer is going to teach you a lot more than just hitting Tab on autocomplete or GoToDef.
Twenty years ago, my IDE was two terminal windows running VI, vertically tiled. On the left, I open header files, and on the right, source files (I was a C++ programmer). When I wanted to look up method signatures or member names, I would quickly look it up from the relevant header file. This could take anywhere between 5-60 seconds.
If you remind me that IDE features could improve this by a factor of 10x, you're absolutely right. But if we apply Amdhal's law to developers, their biggest time waste is often not the physical act of coding, but fixing logical and structural issues in their programs. And the best way to prevent such high level issues is for the programmer to have a good mental model of the code base.
So, the possession of a good mental model of your code base obviates the necessity (but not the convenience) of IDE features such as autocomplete/intellisense. But at the same time, reliance on such features hampers the formation of a good mental model.
My current compromise is to rely more on the "Go to definition" function than auto-complete.
When someone tries to repeat that from an IDE/LSP perspective, it obviously feels slow, akin to speaking a new language for the first time. Does one need this language? It’s hard to tell.
Problems show different contrast to people with different backgrounds. My personal position here is that if you shouldn’t solve non-problems. The set of problems you decide for yourself based on your experience.
I’m a balanced guy, btw. Sometimes I just sit and try to force upgrade my dev env. What sticks sticks. What doesn’t, goes to hell. I’m using ALE (nice multi-LSP tool) and all the tools I know since ancient times. Copilot writes what I call boilerplate, and I neither trust it nor have the need to write it. Passing my real-business functions to LLMs usually produces unfixable mess, so if someone knows another way for it to be useful, I’m all ears.
Your brain is a good natural limit on complexity. I'm not saying there aren't goals that merit tools to manage complexity beyond what your brain could, only that you should be extremely reticent to unshackle that monster, because it's more likely to eat you than do your bidding.
Or more practically (and maybe more of a polemic): if you're maxing out the capabilities of your IDE on an average project, you goofed! We built postgresql, sqlite, Redis, Linux, Go, etc without IDEs.
Syntax wasn't really graded, its forgivable to make trivial syntax errors on pen and paper. The questions never covered more than a few dozen lines of code. But doing it this way forced us to really actually hold an entire small problem in our heads instead of just whatever slice of it the IDE was helping us focus on at the time and I think developing some of that skillset ended up being hugely useful to me when I started my career and was jumping into codebases far larger than I'd ever navigated before.
Jump-to-definition and search for symbols in RubyMine/IDEA are some of my most used tools today, but I think I'd probably be a worse programmer if I never had to develop the skill of working without them to a certain extent. I'd encourage everyone to give it a shot next time you have a few minutes to sharpen the saw. Grab a yellow legal pad and write a little toy parser/lexer for a toy programming language you just invented.
FWIW: I also love printing out diffs for nontrivial code reviews and marking them up with pen and paper, particularly big SQL views.
Added: it helps a lot to have real documentation, something that has gone out of fashion in recent years. Maybe these IDE's are being used as a substitute. I hadn't thought of it that way before. Hmm.
Added 2: Also, Emacs has always had TAGS (this quick navigates the editor to a function's definition given its name) and I do use that sometimes.
When I see someone using these features as part of their workflow, I semi-confidently predict that they spend the majority of their time working on a single codebase, such that the time investment to get everything working was worthwhile.
If, like me, you work on multiple different codebases most weeks, it rarely makes sense to even try setting those things up.
I actually remember all the relevant fields and I am currently working on, or just reread the relevant code when necessary.
I wonder if syntax and cute grammar has any relevance in a world where people basically let the IDE to the coding.
I honestly wouldn't call me a good coder if I wasn't confident I could produce the same code quality on a computer with just nano or vim and no internet.
ctags has worked since the early 1990s.
Check out this video: How to do 90% of what plugins do with just vim.
https://youtu.be/XA2WjJbmmoM
Code search (ripgrep, GitHub search, Sourcegraph, git grep, or even just plain grep). You can use VS Code search but I prefer CLI tools so I can filter the output of my search with more searches—VS Code makes it hard to post-process/filter project-wide search results.
Filename search (fzf, fd, or just find).
There are cases where LSP-powered Go to Def is faster, but there are also cases where it’s less accurate. I’m talking:
- Untyped code in a graduate typed language
- References of a method name in a YAML file pulled out with reflection
- References of that method in a comment of some other method, where that method is actually the method I’m looking for.
- A one-to-one mapping of method names with some enum somewhere else, but lowercase in one spot, and all caps in the other spot.
So yes, sometimes go to def will be faster, but you’ll lose out on so many other possible references.
Another case: repos where the codebase is so large that the editor tooling is slow. Repos where I’m brand new to it, because I just need to check why some code in a third-party library is going haywire.
Grep (code search) is just so powerful.
I have a post about how to do large-scale code migrations/codemods. Everyone assumes the post is going to be about how to use high-powered, language-aware AST-based static analysis tooling. But half the post is talking about the unreasonable effectiveness of regular expressions.
I just use a TAGS file for definition lookups. It’s fast to generate. Every editor worth its salt understands the format. It’s fast.
It’s perhaps slow but I do tend to use my own memory instead of autocomplete. As I use a module more frequently I tend to recall it from memory and know what I want ahead of time. So eventually I get faster than autocomplete. And bonus I get to know the codebase.
I find I make up for the initial slowness because I do most of my thinking before I hit the keyboard. Sometimes the autocomplete is faster than I can type but often it elects to select the wrong thing by default and it takes me more willpower and key presses to select the right one than to simply type what I want.
Slight gains at the keyboard don’t equate to much for me.
Although if you watch my streams you can see me smooth brain my way through stuff without much in the way of IDE tooling. Often when I’m streaming I’m programming off the cuff, chatting with folks, and generally more distracted than I usually am when programming alone. I also look up documentation manually a lot either because I want to show the stream or because I’m so distracted I have a memory like a fish.
Over time though I prefer to internalize and learn than to rely on tools to do the thinking and recall for me.
I don’t really use AI tools. I find reviewing the code mentally exhausting and tedious. I prefer to simply write the code I want rather than try to clumsily tell a robot how to. Plain language is sufficiently vague for conversation and too vague for programming tasks.
Short comment regarding syntax highlighting, unless you have some impairement, I think everyone benefits from this. It's something that helps you recognize patterns much faster and you can skim through the code way faster than without.
IDEs tend implement features quite ad-hoc without thinking how they compose which limits the workflow to certain style that's not always very ideal.
Go-to-definition: the band-aid for overengineering. Write 37 levels of indirection, don't pay the price.
AI programming: now you don't even have to pretend you understand the code! Bonus point: sometimes it generates from StackOverflow, but from the question, not the answer.
These are good tools, but it's easy to figure out who uses them to help themselves, and who couldn't write code at all if it wasn't for these tools.
The LSP is a (mostly) deterministic system that surfaces information provided by the language or by other programmers (types, comments, etc) which are meant to save you time and reduce the cost of context switching.
The other is a completely non deterministic system which generates code from thin air.
That would be like comparing GPS to a self driving car. Both are certainly helpful aids but using one is not like using the other.
Yes, what you've described, I also went through something similar.
I've been programming little over two decades now. Been an autocomplete (ctags, intellisense, lsp) user throughout my career. Never had any real problems with them; they were convenient tools, nothing that different from someone using grep/fd etc.
The codebase I (used to) work on is a 30m+ line behemoth, and API frequently changes underneath me; an LSP is crucial to get the work done. How could anybody keep two dozen+ minor-naming and subtle-semantic variations of the same method in their head? I'll say yes to auto-complete any day of the week, I thought.
About two years ago, I've noticed some worrying cognitive signs. Out of the blue I realized I could not remember the name of methods, classes, interfaces, even the ones that I use daily. I could code, there wasn't any problem with that. But I could not write anything down without auto-complete. I couldn't even fill the arguments of a function without the LSP holding my hand throughout the ordeal.
With the realization that both my grandmothers went through dementia/alzheimer's, I truly felt like walls were closing in on me, in real time. Of course, I went for a check up, which came out clean, but I could not shake that feeling of impending doom.
By luck ---and some hefty dose of depression due to unrelated personal reasons--- I started writing a toy compiler in C, something that I had no experience with whatsoever. With just a text editor, because I dreaded having to install visual studio on a two decade old computer (which was the best I had under the circumstances). Despite forcing myself to fumble through, a few days later I noticed that the entire code base was in my head. I knew precisely what I wrote, how I wrote it.
Life went on and I went back to work. And only then I noticed. I was waiting for the LSP to catch up and fill the correct type, my mind went back to my crappy lexer. Programmer? No, I felt like a factory worker on an assembly line.
A month after that I finally gave my resignation, then started a new (self-employed) programming job. For the past year I've been working on a game + an engine, without intellisense or LSP. In fact, I even disabled syntax highlighting a few weeks in, and never turned it back on. I've ditched quite a few of my regular tools, opting for simpler (often homemade) alternatives; simple bash script instead of cmake for example.
Suffice it to say, I've come to some personal conclusions. These conclusions stem from my personal bias, true, but I do feel strongly, that they apply widely to the field as a whole.
In short:
1) LSP, intellisense, code completion, are overall harmful.
2) Syntax highlighting does not work (most of the time), it just satisfies the part of our brains that like to recognize patterns.
3) Complex and highly integrated tools are a net negative unless they are purpose built.
Most people will vehemently disagree with all of the above. That is fine; I'm not on a crusade against the machine, so to speak. I just wanted to share my view since I resonated with the post I'm responding to. But I will still briefly explain what I mean, in case if anyone's curious. (In part 2, because comment was too long)
1) LSPs. They are probably most productive tool in a programmers belt. Even now, if I had to work on a giant amorphous code base that changes on the whim of hundreds of individual maintainers, I would use an LSP. That said, in my opinion, they prevent you from forming and conveying your actual thoughts.
Using an LSP is like having your sentence be completed by someone else. It's like riding a scooter everywhere. It's like talking to people that you always agree with. Fine, in isolation, but forms habits of dubious benefit at best, or downright harmful at worst. Our brains are just like any other part of our bodies; if you don't use it, you'll lose it.
Completing a function name? Sure, it makes you faster. That is what you wanted in the first place. Pressing 3 keys at most then hitting tab, how could that be harmful over typing the full name with over 16 characters? Unfortunately, brains excel at optimizing, and when done enough, your brain too, will happily optimize the name of the function away.
Smartly completing a function's parameters? Sure, invaluable honestly, without sarcasm. Being aware of types? Even better. Over time though, the brain will strip away the unneeded.
Naturally, this convenience doesn't make you unable to code. The necessary information is still in your brain, just at a higher level. You may not know exactly what gets returned from somewhere, but you can change a few <>'s add a few 's, perhaps shuffle the name a little bit and still get the correct type, because LSP will color it correctly when it is correct. Did you name the thing nioseLevel or levelNoise() or getNoies().Single(perlin-2d)? The idea is the same, does how you get there matter?
In my highly personal opinion, typing every syllable every letter, thinking about the correct types before writing, knowing where a structure is, knowing how functions interact and so on, is the primary way that our brains interact with the code itself. Quite literally it is the exercise that your brain needs to stay fit. This interaction creates a mental map initially, and eventually leads to mastery by being able to hold everything in your head.
Reading code is different. It is a passive action. We get ideas through observation, but learn by doing. Typing, thinking, then writing is the doing verb in programming. Reading is the observation, the reflection after the fact.
An LSP strips away the necessary weights, if you will. If you ride a scooter everywhere all the time, no one will gasp when your muscles atrophy. LSP is similar, but compounding. With the rise of LSPs, so came the rise of complexity, often in form of bad architecture. My personal (and I will immediately concede that it may truly be a personal physiological problem) is the rise of complexity in often what should be pedestrian code. Reminds me of the worst times I've had with java; because everyone has LSP's (right?), who cares if you need to follow 5 files just to correctly create a mental model of that type? But hey, at least it's not oop (because it doesn't have the keyword 'class' in it).
The more I type the more I realize how hard it is to concisely explain what I mean. If I were to use an analogy, using an LSP would be like talking to your best friend where you complete each other's sentences. Yes, both of you know what the other thinks. When you see the news on X, Bluesky, or even TV, all you need is to look at each other and shake your heads. There's no need for words, one glance is enough. If your only political discourse is two best friends agreeing with each other, if you've never exposed yourself to opposing views, you have essentially never articulated your thoughts. Your ideas remained as a bowl of feelings, not logic. So, when someone with an opposite view and a glib tongue tries to debate, they will verbally run circles around you. And you will get angry. But the only way to get better at debating, is to debate. Watching debates won't give you the expertise. Your friend can't hold cue cards as you talk to strangers (or at least they shouldn't). You do not get better at eloquent speaking without speaking. You do not get better at eloquent writing without writing.
To me, LSP was like that. I vaguely had a feeling, and I let everything else go --- even the syntax --- to achieve it. Once collectively done, the result was a 30m+ loc mess of a side project; yes, not even the main one. After years of that, I came to realize I wasn't programming as I defined what programming was to myself.
In a more broader sense, LSP enables complexity. Programming is an endeavor where complexity occurs naturally, and simplicity has to be fought over. This creates a vicious cycle where you need an LSP to combat the complexity, which itself leads to complexity, and in turn leads to more LSP usage. Our field makes this is even more noticeable since programming itself is in a state of permanent newcomers influx; the demand for programmers goes higher every year, yet we are persistently and woefully unprepared for properly educating these programmers, because we can't even agree on what "correct" software development looks like. In this sense, the "harm" is more institutional, rather than intrinsic.
I would not advocate or even advise others to stop using LSPs, mind you. But I do think it is overall harmful at a personal level, and at an institutional level. Not that it would stop any billion dollar companies of course.
2) Syntax highlighting. Throughout the years, I have seen exactly one example where syntax highlighting could (could) have actually prevent a (singular) error, and that was with C #endif's. Leaving aside the extremely self-evident nature of the C macros, the code itself had mixed quite a few #endif's and the author could not be bothered with adding a comment, because (I'm paraphrasing) "his LSP darkened the #ifdef's correctly." Mine did too, but that's beside the point.
Disabling syntax highlighting was unexpectedly difficult, far more so than leaving LSP at the door. At first I couldn't read anything. The code simply didn't look appealing enough. Everything about it was off. I just shrugged and continued my little experiment. Eventually, I realized lack of coloring and hinting made me focus more on the code itself. The pretty patterns while great to look at, I think they do lead me to discard sections of code at a personal level. I would never say turning syntax coloring is better, just that it doesn't have any benefit that I could substantially measure. Though to be clear, turning them off really did gave me some satisfaction, most certainly borne from the mindset of making a change, rather than change itself being positive.
Whenever I look at something on github, my pattern matching brain immediately shoves a dopamine hit down my throat, so yes, I still think syntax highlighting is prettier. Though at the same time, I still get the feeling that I pay more attention to the cold and colorless columns of my editor.
3) Over time I have developed a certain distaste towards certain applications. And over time, that distaste have evolved into a preoccupying hatred. So I will refrain from saying too much about "complex and highly integrated tools." I've come to think some of the reason why these tools exist in the first place is the commoditization of programmers. Nowadays I prefer far simpler tools, at least conceptually speaking. I did pick up emacs after I quit my previous job, so there's a hefty bit of subjectivity in my assessment. That said, I would choose notepad (literally notepad) over visual studio at this point. Bash over bazel; a gun over typescript. And so on.
Wrapping up:
Am I a better programmer than I was a little over a year ago? Without a doubt yes, I have improved substantially. Though the improvement isn't what you would think. I'm not "seeing the matrix" so to speak, as a young me would've embarrassingly put it. At first I ended up regressing and kept refactoring my tiny codebase; it was a slog. Over time I found what worked for me, and become able to hold almost the entire codebase in my head. No, I did not gain inhuman memory simply because I stopped using an LSP, I just became more aware of the code structure, and gained the ability to reason about the entire codebase more clearly and precisely. If I were to present two code samples, one from two years ago and one from today, perhaps even I wouldn't be able to say which is better at a micro level. The real difference is that after numerous refactors, the 200k+ loc codebase I have now feels like a 2k project from my college days; easy to modify, easy to keep it all in my head. Thinking back at my career, most of the codebases I worked on could've achieved that too, but chose not to. The overarching reason is --- in my opinion --- the commoditization of programmers, which causes institutional loss of knowledge. LSP isn't the cause of that obviously, though it does worsen it.
Years ago, I worked for a long time using Smalltalk (VisualAge Smalltalk, to be more specific). Older Smalltalk versions didn’t have autocomplete. But it wasn’t a problem because Smalltalk has excellent code navigation features: find implementations of a message, find callers, and evaluate code inline. With those features and some code conventions, I never felt the need for autocomplete.
Perhaps it’s my Smalltalk legacy, but nowadays, I use the most Cmd/Ctrl-Click to navigate to the implementation, read the sources, and use the “find references” feature. I don’t know if the LSP implements those features, but reading the sources gives me much more information.
Before programming in Smalltalk, I did some C++ and Java programming. While all Java IDEs had autocomplete, C++ autocomplete was unreliable on most tools I used. The solution is to read the docs and the source using search tools across the code base and third-party sources.
first time i use an editor which have auto completion but it works only for same file, it was time saving thing.
then i see real auto completion feature, full function name, parameters, returns and couple of words from manual, it was mind blowing for me.
then i start using eclipse, with one click whole class generation from interfaces etc. one of my friend create its own snippets and it was writing like 3 person, couple of keyword strikes and bamm, whole thing is ready.
then internet become something like air instead of water, constant flow of information, constant needs.
For me, Ruby's standard library tends to use names for things I would guess first, and the way people build things in Ruby feels natural to me. Any sort of LSP feature is nice obviously since it's never in the way, but when I end up trying Solargraph or RubyMine I end up noticing I'm just not paying much attention to it. The only places where I end up reaching for it is mass refactoring, like big renames... which are tough in things like Rails projects where files need to be renamed and pluralization matters.
So for me, it's the ergonomics of the language. That's probably mostly related to hours spent with it of course, but I think everyone has some specific environment/language/style that lets them compress the current state of what they're working on in their head better than anything else.
You just learn to memorize a lot, organize and design your code. It feels very similar to learning to navigate a city without map. The map will be in your head. A very similar feeling I had with code bases. I navigated a map, sometimes I knew the route towards a piece of information, sometimes I knew exactly where it was. Sometimes you add a building or a new road, but you all keep it in your head.
Nowadays I use LSP, autocomplete LLM's and what not. Makes things easier and above all faster.
I still can do without, I am just a bit slower. For small changes and simple scripts I fallback to vim. For git I still use the terminal. And I still use a lot of commandline utilities, because each IDE has a zillion of commands that do the same anyway.
For your example, nowadays I would use grep or some modern equivalent to search for definitions. And to be honest, that isn't that much slower. I can have my editor open in tmux tab and in the other my editor. You don't have to jump through files then.
As for copilot, I find it impractically distracting and I do not understand how people are supposed to code with it tbh. While coding I spend most time thinking about the code than just typing, and having random autocomplete suggestions popping up with lag while typing disturbs my chain of thoughts. Maybe if I was writing more boilerplate I would have liked it more, or I am using it wrong somehow, or maybe one gets used to it after some time. I sometimes use copilot chat though.
There are a non-negligible number of my coworkers who don’t use the licensed LSP implementation and they write all their code in vim – or worse, gvim through a VNC. It’s very easy to tell that their code quality is worse.
I have these features on (more or less) and I still use grep, ag (the silver searcher), and GitHub search. I often need to look for things my editor can’t possibly figure out. I’ve never found editors to be great at this. Then again, I probably haven’t optimized for it. I’ll admit that might be a mistake. Old habits die hard sometimes.
BTW, I’m usually looking for other people’s work or code I wrote long ago. Functions written somewhere in a vast code base that I don’t have completely checked out (because I’ve never coded on parts of it). I don’t think much about the obvious stuff, like a function in the standard library.
How long you have worked in a particular language affects how much you will be looking up function definitions.
Lots of other people working on the same project with loose standards means harder time assembling a mental model of the codebase.
Someone with cursor asked to program in a new language may be like “ah ok whatever sure” vs someone with vim might be like “it’s unreasonable management wants us to use these new age tools X works just fine” and then say “vim is so fast” in the same sitting.
So what using an LSP is doing for you heavily depends on the kind of code you write. Are you the kind of dev who works in pretty much a very familiar codebase for a very very long time? Then LSP could even be impeding acquiring a better mental image of your code. Conversely, if you are a coder who often ventures into different and unknown parts then LSP might make you way more productive.
2) Depending on the language, LSP can become an actual part of the language itself. When I code in languages with good LSP (C#, Rust but also C++) I often code with the LSP as my "target".. I think what I'd like to see popping up when I type a "." . In this part of the code I want to see this var and this method.. in that part of the code I want to see also this other vars and methods it gives a very good idea of what the "surface area" of a piece of code looks like.
LSP are also very good instant feedback if something you are typing is wrong. I press "." and nothing shows up? I have some error somewhere and/or the thing I am dotting is not what I think it is.
But, in order for this to work the LSP has to be REALLY good to the point that has to be close to zero doubt that if the LSP is not behaving it means it's your fault. Sadly, not many LSP ever reach this level of reliability.
This is one of the thing that made languages such as C# and Java so popular: the ability to "dot" your way through unfamiliar libraries with great ease without having to dive into documentation to discover there's a function X in some file Y that already does exactly what you are trying to do.
This is the kind of question where you can easily go get a much better and more comprehensive answer on your own by experiencing it yourself.
On the whole I think I prefer my old setup. All I really need out of my IDE is decent syntax highlighting. I've got devdocs.io for Ruby and most Rails definitions and I know how to spelunk through the Rails codebase for anything that misses. Go-to-definition is handy for my own code but I generally know where it lives, and if I've forgotten there's always ctrl-shift-f or `method(:blah).source_location`.
in order to be extra information for me, the backup camera display screen would need to be in the back seat of the car, where I'm looking while I back up
I still have most of the python standard library and many of the interfaces I use often in my brain, so autocomplete saves me almost no time. I can type working code into notepad about as fast as I can type it in VS code.
I can back up without a camera, and I can "drive stick" as Americans call it. But a teenager learning to drive today should maybe not bother.
But that's not the same question as "why aren't you installing a backup camera?". Because it wouldn't change my ability or speed of backing up. And I can drive stick. Why not replace my car with an automatic? Because it wouldn't improve anything.
In fact I dislike automatics, since they'll suddenly and jerkily decide that another gear is best, ever though my foot didn't move, and inclination didn't change. But in a couple of decades it'll all be electric and this problem will go away. The deliberate engine braking is in electric cars just the brake pedal.
If I was back working on a project like that, I would use whatever IDE everyone else was using. It makes no sense to fight it. But I prefer projects to not be designed for/by IDE, since that tends to make everything more readable and easy to navigate. I prefer to be able to navigate around the source-code on the command-line or use whatever generic tools to browse, and not be forced to ctrl-click my way around, and if (almost) everyone else working on the code wants that as well it is less likely that someone ruins it.
That said, I have at times also set up Emacs to use LSP or other built-in code-navigation tools, or templates (yasnippets) for generating boilerplate, for some projects. I rarely end up using it much. Usually I just jump around using built-in tools like M-x vc-git-grep or M-x rgrep. The code has to be pretty heavily designed-for-IDE to make those simple tools slow to use.
Working in a code base every day I end up creating a mental map of the code and the data, so it's not hard to find stuff I'm looking for. When that doesn't work, a global search or find/grep helps.
At the start of learning rust I used a bit more help from the IDE, but now not anymore.
For me it is just easier to have a much deeper knowledge instead of "only" have knowledge about the tools. A good example was when I used SVN back in the days, I only used a GUI and always struggled if something does not work out. I had basically no idea, what is really going on. So when Git became the new normal I forced myself to use the command line only and because of that I can always help myself (with Git atleast :D).
Certainly not something for everyone, but it works for me :).
1) regex be it grep, or equivalents built into the editor. 2) Multi-cursor editing. 3) dumb autocomplete. 4) knowing how to type fast.
About 70% of the code I write at work is C. Another 20% is Rust (which does have a nice LSP implementation that I use, though I wouldn't call it essential), and the remaining 10% is the usual bash/Python/Ruby/cmake/make mumbo jumbo.
Before I figured out how to configure Sublime Text (I used Atom until it was killed) to have JUST go-to-definition and no other LSP features enabled, I'd just do a quick couple of searches across a code base or file. It was fine tbh. Yes, a single key press is much nicer but it really wasn't that annoying to not have it. I got just go-to-definition working and was able to turn literally everything else off and I'm really happy with it.
For context, I've worked in all kinds of code bases with all kinds of quality and organisation (including lack thereof) in a variety of languages and frameworks, and I've not really felt the NEED to change. I do try VSCode and/or using more of these typical IDE features maybe once a year in an attempt to leverage these features that seemingly every other programmer feels they gain a lot from but I just HATE it. I can't stand it. I really have tried! I do assume I'm leaving some quality of life and productivity gains on the table but I've never been able to make it work for me.
I'd say it does. Particulary for names based on English words with synonyms, if I recall the wrong synonym for this particular library, at least autocomplete is faster than searching the docs for all the synonyms. But not as fast as scanning the method/function list should the docs have that (and far too many miss it out).
If you can remember which word for the concept the method you're after uses, then you don't need autocomplete.
BTW only Jetbrains (and other heavy Java IDEs?) get autocomplete right IMO. The basic form in VSCode doesn't cut it in my experience, and gets in the way.
You're spot on with jetbrains etc.. I have also tried it and a few other highly specific and focused best-possible-experience full IDEs along the way. Early in learning a stack I felt needlessly burdened by the nuances between autocomplete options (which put me at risk of ending up stuck in an documentation hole happily reading about shit I didn't need to for hours) and once more confident I just went back to regular annoyance. In some code bases I'd probably have used it more, as the suggestions were so much more refined and useful. You're totally right in that it's a whole different league from VSCode.
My vim configuration is moderately customized, and I regularly write code on systems I SSH into since my personal laptop is a Pixelbook.
The main things I rely on are syntax checking plugins.
I use ripgrep to find stuff in codebases, and when I'm working seriously I use an ultrawide monitor with plenty of screen space to have documentation I can glance at open.
I have a significant collection of small libraries I've written for common tasks, and often refer back to my own code across projects to figure out how to do things. Metaprogramming is something I do often too.
As another person said, thinking is my main constraint when working, not actually writing the code.
It should also be noted that I'm not primarily a "Software Engineer". I'm a Principal Security Engineer at a big tech company. If I'm working on actual code, it's generally "interesting", either a hard problem at work, or a personal side project. There tends not to be much boilerplate.
For TypeScript/JavaScript in particular, something like copilot would get in my way, as my style for this languages is extremely non-standard.
I'm not sure how much any of my coding habits have to do with it, but I regularly tear into unfamiliar codebases to fix bugs and add features, and I'm pretty good at orienting myself quickly, even in languages I haven't used before.
The first thing I did with Rust was add features to a data compression library without any prior exposure to the language...
I think that the reasons I’m not using IDEs/LSP are more worthy sharing:
First one is that I cannot use one. While the main language I’m using has relatively ok LSP, the codebase prevents usage - as it’s ridden with circular dependencies, macros, module aliasing and multi dispatch cast chains. Everything outside of syntax highlight it unusable.
Second: I noticed that assisted coding is like GPS assisted driving. I stopped internally understanding architecture of the code when using IDEs, this disconnected my mind from the code and at some point it started to feel like shoveling. Mindlessly spit out the code at let IDE handle the rest. That made me efficient at local change but unable to build up and see „big picture”. It bothered me to the point I decided to not use them anymore.
The third thing is that I don’t take „no” (coming from software) for an answer. I often get those specific needs for code search/refactorings that IDEs/LSPs refuse to work with. E.g. I need to search customized OpenAPI specifications and link it to a macros/generated code for browsing. IDEs/LSPs cannot help as it’s not idiomatic, so I customize. And customization is a loop. You customize, you learn how to customize better, you customize more etc.
When it comes to LLM-based aids I’m at point of minimum trust. I’ve experienced so many problems with that code that I rather just disable it entirely. Example? I was setting up Ticker based waiters for Go and then in one place I accepted `time.NewTimer` local-LLM snippet instead of `time.NewTicker` in infinite loop. It was 4 lines of code and looked VERY similar as in 3 functions above (that was before code reduction phase). For those unfamiliar: Go’s ticker ticks every N time. Timer fires once. One-shot-fire in infinite loop == lock. Took 4 hours to debug.
I’m not against using LSP/IDEs. They have their use (but for Pete’s sake ask your organization leaders before feeding proprietary code into ChatGPT). They don’t work for me, I feel dumber. Maybe I’d be more efficient but I don’t care. I do this job also for fun, and GPS kills the fun part for me.
I've written a ton of C# and I don't remember shit about methods in the standard library, because it's all there, and getting worse with AI. Not to mention the detrimental effect that the constant interruptions must have.
I use IntelliJ for Java, but since I learned the language and standard library long before fancy IDEs were a thing I barely notice the blinkenlights.
Personally I often write code without LSP, and use pure text completions with Emacs (words of opened buffers) and cycle through them. I know the language well enough, or I look things up in their documentation until I know them well enough. Occassionally I will also use LSP, if it exists and is easily configured and works well.
Go to definition: If I don't have it, I develop a map of the project in my mind where I find what in a project. I have also observed how projects become not so well organized, when people rely too much on always being able to "navigate through code". One does not need to pay much attention to module naming or class names and the like, if one never na igates the file tree to go to the file. Sometimes I have to rgrep/ripgrep.
By default it will give all the identifiers in all open files. Having a few of the relevant files open in the editor will get you pretty far.
I do use LSP in other environments where it is available but I still do a lot of my work with just plain vim because I jump between code bases a lot and setting up LSP for C/C++ needs some extra steps to work.
I just… write code? If I need to remember what’s in another file, I open up that file in an adjacent iTerm pane, or pull up the docs for the library, etc etc. if I want to have the program run every time I save, I run a watchexec in an adjacent pane.
is this the most efficient way to code? probably not. but it involves zero yakshaving, which I no longer have the patience for.
I have etags for jumping to a definition on the rare times I need to do that.
Typically I'd leverage 2 large monitors and keep at least 3 editor "views" into code open at once: one for what I was actively working on and 2 others that would contain reference code. You also take considerable care as to how you organize the code, so that grepping for definitions is easy. If I needed to see how a particular object or function worked, I'd pull it up in one of the auxiliary code windows - which would typically start with a grep alias command that would have a shortcut to open it in a designated emacs buffer window. Not anywhere near as convenient as an LSP but it got the job done.
I didn't know about them, and I had spent a lot of time with the documentation and code of everything I touched (language, dependencies, etc). Looking things up reinforces this. The value of a feature such as autocomplete is marginal to me as a function of the effort I have to make to learn how to use it properly, the frustration of using it improperly, and the added steps to fix a blunder, so I just don't bother with it.
I also have a habit of having the documentation/book/code of a dependency open nearby and doing my look-ups there.
A lot of time is spent refining existing code, debugging, optimizing, refactoring, etc...
Makes me sad :(.
At least xdebug works.
*EDIT:* updating to address "CoPilot" specifically, as I don't think it's the same as the other things on your list. I have reviewed a lot of submissions that were written with various AIs and all of them without fail, this is not hyperboly, all of them were just bad (I'm sure someone will say that if you just use it for a 1 line error tweak it will be fine and what could go wrong there? And sure, probably, but if that's the case you're not getting any benefit from them so that's not what I'm interested in). The code was worse than a human would have written it, there were subtle bugs that no one had noticed because they were trusting the AI, etc. just don't use them. They're not ready, they may be speeding you up, but they're slowing down whomever is reviewing your code. It's a serious disservice.
And I’ve noticed my co-workers who use IDEs and multiple monitors are no more productive.
Later, I found out about ctags and started using it; but it's not that useful for autocomplete. I kept to my old naming conventions: one verb whenever possible, plus any prepositions (is/has/etc.), noun if applicable, and module prefix.
This works well for the programs (not libraries) I write. Libraries have a different set of design considerations.
My function names end up like:
Most functions should end up private (static/not exported) anyway, so exported names can be short and still unambiguous.In languages that have namespaces like Go or Python, I use their own namespace facility. I like to avoid the need for underscores or capitals in names so I don't have to press shift to type them, but some languages (Go) make this unnecessarily hard.
Eventually, when I started working in large Python and Java codebases written by others, I had to get an LSP plugin so it would take me to the correct definition. Ctags doesn't know about overloads. And some people don't care if they put a function in a logical place or not.
My editor's plugin doesn't have autocomplete, but giving functions a reasonably-sized name (and having two code windows open side-by-side) solves that problem.
>So to the devs who don't use these tools, how do you do it? Do you just remember every type and field in a codebase? What does your flow look like?
You don't have to remember every field and type, because you don't work with all the types at the same time. Ones that you work with more often, you remember.
Workflow is something like this: open the file, split to left and right to see two stack frames of the same code path, open another terminal tab, start test watcher. Make changes and see tests fail. If tests don't fail -- write more tests. Then write more code. The usual TDD struggle. In case of CSS, just change it in the browser, then copy back to editor and refactor a but, then check again.
When in doubt about spelling, press shift 8.
>What do you do if you need to look up the definition/implementation of some function which is in some other file?
:open another file and search through it, quote simple. How do I know which file to open? I know and when I don't git grep goes brrr. In theory you can jump to definition even in vim, use fzf to open files faster, in practice, none of that is not the limiting factor.
The limiting factor is iteration cost and the size of your context window.
That all being said, I use vs code now (at work) and use jump to definition all the time, because typescript support is just better in vs code. I think I started using it the moment I had to open more than one repo and switching between them on a regular basis.
Then I get back to my own fun stuff and there I use vim.
I think co-pilot is the more interesting example you give, because my answer is that I will not use it unless I could do the job without it. For example: yesterday I was refactoring a bytecode interpreter as the core loop had grown so large it was causing a performance issue. Solving this required getting the size of the method containing the inner loop back below a threshold. This isn’t something an IDE’s “Extract method” refactoring can do automatically because there is mutable state that needs to be passed in and out, and if I didn’t already know how I wanted to do it, would not be something I could easily check the correctness of co-pilot doing. So you sit down and think. What stages will you have to go through to extract and change this code? You come up with something that will let you validate the idea and then refine it, and you have tests, lots of tests, and some degree of intuition so that when you see a failure you can guess the thing you might have missed.
This setup is agile but still not as fast as lsp or copilot, but I prefer learning and remembering over speed, personal taste :-)
When you're working in one area of it, you read it and make additions in it repeatedly, and eventually your familiarity increases until you have mastery.
Then you can code anywhere, anytime, and merely type it in when you get back to the keyboard.
It's like learning a (human) language. As an analogy: "[foreign language] speakers who don't use a dictionary, how do you do it?" But the foreign language is your codebase. Learn it.
A huge 100k LoC codebase? I'll happily boot something with an LSP. But it's very annoying and gets in the way. I hate when the context for my keystrokes is an autocomplete window, but it's worth it if I need to call a method with fourty arguments.
But when I'm writing code from scratch, it means I know what I wrote and what I'm using. If I forget, I usually just go back through the code.
Pen and paper is very useful as an extra scratch space.
I don't advocate to do the same outside of the learning process, of course. The tools help a lot (in fact, LSPs are awesome), but there are two principles that are important here I think:
- having an understanding of the underlying code, language, etc.
- being intentional about the use of the tools (e.g., invoking auto-complete with a keystroke vs an endless suggestions list; running a linter explicitly vs a code bloated with "insights" and "warning highlights", etc.)
The concept of IDE was invented by lisp machines, and emacs inherited a great deal of that functionality, but without the visual IDE. Great programmers were using shortcuts for accessing documentation at incredible speeds. The go-to-definition was there although limited to supported languages only, usually lisp dialects.
What Microsoft did was popularising it, standardising for different languages and making it easy to use/visual-graphical interfaces and destroying the competition the MS way, like Borland.
I would say, except copilot, all those features existed 30-40 years ago. It was just a pain in the a$$ to set or very expensive(Lisp Machines IDEs) or difficult to pirate(very important for Microsoft success).
Copilot can be replaced by competitor's products, like Claude. I use them outside Microsoft ecosystem. I use my own automatic system to access the AI.
If you need an IDE, that just means your code is a mess.
Ironically, the more messy the code is, and so the more you need the IDE, the more likely it is that the IDE will have trouble coping with the code, becoming extremely slow, or randomly failing to jump into some functions.
Ensuring your codebase remains workable without an IDE is actually a good litmus test for quality.
My main language is python and for that I use jedi-vim (which allows me to jump to a definition), fzf with ripgrep to easily open files and search for specific things, and ALE that runs ruff formatting and linting. Tmux to keep my terminals organized and openbox to make it stylish and stay out of the way.
No LSP, no autocomplete, and sure as hell no AI nonsense.
You're emotionally invested. Not wise.
At some point, for me, ‘find <dir> -name “*.ext” | xargs grep <pattern>’ took over for recursive grep, because the required tools are available on most Unix systems.
Over the past few years I rarely write code anyway I just use ChatGPT and then edit the code.
I tried using the new canvas/project features last night and I think it slowed me down versus a copy/paste workflow. I think those formats could be good but they're not fast and polished enough and I've gotten used to tricks and hacks for working in chat style.
I've never used a LLM that has actual access to the codebase, so I kind of take chunks of code and put them where they need to be. Even if I have like 6 lines of code and I want to change the logic I past that into ChatGPT and say "change this to do XYZ". Even though that sounds dumb I actually gain massive advantage in concurrency. I can be working on lots of tasks at once because ChatGPT kind of toils away and I'll then jump over to something else and come back 90 seconds later and the code is there, I read it over, maybe diff it from a previous version, then I run that to test it and maybe while that's running I go to a different chat and issue another instruction.
Before LLMs I used vim. When I want to find a function definition I ctrl+z find . -name ".whatever" | xargs grep "function whateverThisIs" and things like that.
grep -d skip "thing"
I copy and paste a lot if I'm using function names, I use a clipboard manager I guess as a sort of pseudo auto complete.
"Then why don't you just use an IDE?" I hear you ask!
It's because I write lots of what you might call "glue code" or "microservices". Lots of snippets of SQL and JavaScript functions that are executed on a queue, and API calls to integrate with things and code that runs on different platforms.
I'm almost never working on what you would call "an app" these days, so that's probably why I've never gravitated towards an IDE. Even when I was working on more monolithic web apps I never used anything other than vim with "set nocompatible" turned on so it wouldn't try to do anything IDE like.
I think what I liked about it then was that I could be just as productive using putty from an internet cafe as I could be on my own computer. Like a doomsdray prepper version of coding.
Is it a better way to work though? Well, in some ways, it forces you to use more cycles in your head, so you do gain proficiency, and you can spot mistakes more quickly. This is a skill.
But once I started working on larger codebases and higher complexity code, I felt it was better to redeploy those brain cycles toward thinking more about architecture, and letting the LSP check the code. With an LSP, I'm now able to write more correct code in fewer iterations.
Like everything it's a trade off. Am I impressed by people who can do complex mental arithmetic? Yes, but for anything complicated that I'd rather not get wrong, I will reach for a calculator. Same idea here.
Every time a popup or tooltip covers code around my cursor I groan in agony.
I’d probably use it if the tool tips just updated in the bottom right of my screen while I typed or something.
It's why I don't like pair programming: done properly per XP principles, it's like having a human version of VB's intrusive error dialog.
I personally don’t like the surprise of autocomplete, or of anything popping up on-screen that I didn’t ask for.
I think Delphi 3 was the first version with basic completion.
I do feel like I have suffered and become a little too dependent on LSPs/AI over time. But it is easy to see why when they can basically write your next line for you with quite a lot of accuracy these days.
My design is usually very simple, I don't have to keep a lot of things in mind to begin with. For instance, https://wordsandbuttons.online/ is about 100KSLOC now, but since it's inherently flat, no dependencies, no third parties, I can manage it with a Vim or a gedit with none of the IDE features. My implementation is always in the same file as the call.
I suppose, IDE makes writing harder code easier, which results in tons of saved time, but it also makes writing harder code easier, which results in tons of hard code.
I liken it to spaced repetition. The first time I don't know where to find something I grep, after a few times I start to remember. I have a vim layout with 12 windows, so I have many views into the code and always have space to pull up more without losing context. I genuinely feel claustophobic with less.
> What do you do if you need to look up the definition/implementation
Sometimes I feel "jump to definition" trades immediacy in favor over natural exploration of the surrounding code. I work on a 10 year old mono-repo with 1000s of contributors. After two years I have a good feel for the basic layout, and would say I have a good mental map of the stuff that most affects me. During code review I'm often pointing people to existing implementations that they've just duplicated.
- Years ago, I didn't use anything. It was just me, Emacs and a few packages. I surely knew about IDE features, but didn't bother. But I did get frustrated to have to look up things every time when I forget something. Autocompletion is an improvement and seeing docs fly up on a function helps a lot. But it's one part of the picture where the computer aids me...
- case in point: rg or ag are fantastic tools for searching through codebases. I use go-to-definition too if I can. However, I usually can find pointed references to fuzzy ideas about things much faster than co-workers who don't use rg, because regexing a codebase is much simpler than tediously running through files I am not familiar with to get a quick answer.
- autocomplete is useful when I don't know the language and helps to spell correctly. It does take me away from learning a language, but I don't do a lot of coding (hope that changes in 2025!)
- when I write Python, I don't really need tools as much. I pull up docs more. I have pylsp installed and sometimes when it is off or it can't boot for some reason, I just grumble and continue to do things without it. The same with bash. I am good with python but stink with bash, but still don't use LSP at all with bash.
- I still have some odd fears that since I don't use dedicated IDEs for most things, I will get disrespected when it is relevant for career progression. This is not without merit - sometimes computer-assisted tools can help with certain tasks much faster than others. We shouldn't measure based on individual tasks, but if you are getting constantly stuck on trying to finish something but the bottleneck is your chosen text editor/environment to convey ideas, it can look negatively.
- please, no copilot! I already feel like I lean on search engines too much to answer my questions. I know I become better when I enrich myself with general knowledge on a topic. It doesn't answer everything (thankfully search engines are there for that and don't want the quality of sites fall by the wayside because of copilot), but I imagine I would not be so swift on the types of problems I solve if it weren't for deliberate documentation reads and discovery.
- I am improving my understanding of Lisps (common and emacs). I feel like there is a different experience working in these. The typical IDE tools feel different given that all these tools can be used to interact with a running environment instead of using something that just primarily analyzes source code. I would probably fall back to my old habits when I am comfortable, but it is fascinating at least to me how the tools fit together differently than in "normal" languages.
Some embedded ish IDEs, ssh-ing to a remote machine where you only have text mode etc.
You'd better keep that part of your brain that can code without autocomplete in shape, you never know when you may need it.
I almost always have a browser with documentation open on another monitor and while there is a speed hit removing the tools I feel like it improved my mastery of software engineering overall.
Easy to say, takes a lot of practice, focus, and knowing common concepts across all languages to execute on.
Imagination, not pagination. Destroy all software. Don't let some tool make you a fool because the safety is off and the training wheels are no longer there. Turn off the defaults, the noise of misinformed conversation, non-existant completion of words lost in translation.
There will be inevitable ultra-basic mistakes, especially if you're also not familiar with testing the code you're actually writing.
I say this as a specialist using an IDE and having had PRs sent to me that weren't even syntactically valid, because the developer was not using an LSP in a language they were not familiar with.
It had many drawback, it had some upsides like really having everything stuffed in my head and learning probably more promptly by searching/memorizing.
All the "smart systems" are equally annoying. The auto-comment generators are just trash. They just add noise like this:
I see this stuff in code review, just did today, I just ignore it now. Garbage added by the IDE, whatever.And then there is the "smart indent" systems that seem to always guess incorrectly.
These systems are like a 4 year old trying to help you wash the dishes. I don't know how you people deal with it.
I coded for years on a line editor (ed). It was probably faster coding then whatever I'm using today. That thing was basically gestures moving at the speed of thought.
grumble grumble, kids these days.
In Emacs I have automatically closing brackets and quotes and such, and if I want to encapsulate something I select the entire thing I want to encapsulate, press the opening character, and it automatically puts the closing character at the end of the selection. It also properly moves over closing elements when I hit a closing character (so if the cursor is in front of a closing bracket and I press the closing bracket key the cursor moves behind the closing bracket instead of inserting another one). I've gotten so used to it that editors which don't do this correctly turn me off big time.
Some have auto-complete, others do not.
I use some of these combos at the same time.
The main thing I learned is that certain languages are made for one way or another.
For example, python, SQL.
There is not much you get from a IDE for python, and a good editor make wonders. SQL is braindead and bad designed, so the only moment you wanna to autocomplete (for get fields) any tool I have used fails (ironically, this one thing where copilot actually improves it).
So, if the language is made to work well without ide, it will work well.
What is a terrible mistake is refusing to use an IDE (or IDE-like capabilities) for languages like Rust, C++, C, etc...
I typically just use plain Vim without much customization. You get used to it. I am somewhat forced to use just plain Vim on most things because I often write code on random HPC servers and have no control over what is available. But Vim is universally available, so I got good at the one tool available to me. Vim does have have Ctrl-P to auto-complete a word, though.
I also have books and other documentation available, when possible. Man pages are actually quite good and available on many systems I work on. For C, you can type something like "man 3 printf" to get basic documentation on "printf". This works for MPI too with things like "man 3 mpi_allreduce". I've been pleasantly surprised at times how much offline documentation is available.
So, we would use SSH into the VM and do our work. This also involved a lot of debugging of code through vim since it's quicker to make in-place edits and re-run experiments, this taught me a lot on effective debugging and writing code for the VM
I also end up better knowing and remembering any new classes and methods because I have to dig through the reference documentation for each of these things.
See lots of comments here about code organisation and developing a deeper understanding of a codebase and I could not agree more. At least for myself not having all these functions gives me a deeper understanding of the code. I’m probably slower to onboard onto a codebase but I bet long term have higher productivity on it.
I remember the functions that are relevant in the current context, yeah. I don't design things that have a lot of properties in the first place; and I'm concerned with what objects can do, not with their types (and I use Python, which empowers me to write this way).
> What does your flow look like?
Generally I keep a terminal open as I write in an editor window; most of the time it's cd'd to the project root. (Hmm, maybe I should be using `pushd` and `popd` more....)
At about 200 lines of code in a file or 10 lines in a function, I start looking for ways to split it up or organize things better. At double those values I start panicking about it.
Every identifier name is an opportunity to explain something crucial about the process.
I rarely use inheritance any more, let alone multiple inheritance. Jack Diederich's "Stop Writing Classes" (https://www.youtube.com/watch?v=o9pEzgHorH0 - a clickbait name, but you know how it goes) is part of my mantra; `functools.partial` is very often a better tool for the job.
> What do you do if you need to look up the definition/implementation of some function which is in some other file?
I can tell where it is because the import statement tells me the package name, and I have a single file hierarchy for code in my project. But most of the time I can just trust myself that it works the way I expect it to, because I wrote it, and I followed conventions when I wrote it, and I made sure it fundamentally doesn't do very much or have a complex signature. (If it operates on complex data, I build that up - step by step - rather than passing all the pieces as separate arguments.)
If it's someone else's code, generally I'm going to look up the documentation anyway, not the code. If for some reason I have to figure out where the code is, I can do it easily enough, because I know where third-party libraries are relative to `sys.executable`, and I know where that is because I use venvs in a standard and predictable way. (Oh, but I could probably improve this with a one-liner Bash function, brb....)
But I do also have the option of checking things at the REPL. Python does a lot to help programmers feel like they don't need an IDE. (Avoiding boilerplate is also huge here.)
- Just try reviewing a PR and see how comfortable or uncomfortable you are compared to when you have the code on your usual IDE or editor or whatever.
- Just try being in a Google Meet or something helping a coworker remotely, and see how effective you can communicate before you have to resort to giving them step-by-step instructions.
One thing is using a feature because it lets you do what you want, but more quickly; a very different thing is using that same feature because you literally get reduced to almost zero productivity without it, and start fumbling around (like in the two examples above). This also includes features like debuggers, tooling in general, or even tech stack (e.g. "why bother learn HTML when React is way more productive and it's mainstream").
It's not about dogmatically choosing one or another; it's about making your productivity floor higher, instead of focusing only on productivity ceiling.
Today, at the other extreme of having an LLM actually write entire methods or sometimes classes for me, I do feel that I have less of a mental model of how my software is designed. Ostensibly I still "wrote" all of it, but it's just not in my mental cache.
Or, that's just age ))
I think I just learn the code base and build a mental model of where everything is and it works for me. I spend more time thinking about how to organize my code well than to actually type it in my editor anyway. It probably helps that I'm working on Rails apps so there are strong conventions about how to do things that makes this easier.
But I DO use jump-to-definition in Vim and other editors. IMO LSP has been an amazing productivity boost just by that one feature.
Try working in a really concise language, e.g. an APL-family one, for a bit. Once you get used to it you might take the style back to other languages, it's possible to write fairly concise code in most languages if you actually try. See e.g. https://github.com/tlack/b-decoded/blob/master/b.c
Generally, I have a hill to climb with a new code base. I get the ideas into my head and then rely on that rather than tooling. It's how I've always worked. I'm very comfortable with my editor so it just does what i think and i don't have to expend effort to translate my thoughts into keystrokes.
That's basically it. This Blog Post by a friend of mine on important editor skills to master is a good read on effectively using an editor https://info.pagnis.in/blog/2014/05/06/10-actions-you-must-i...
I don't use any of those, and I've never felt the need for them really. Is Make and Vim not an IDE?
None of those existed when I started to code at my first dayjob and I've never really seen the value in them.
I find it interesting you think that go-to-definition isn't possible without a language server. I was doing that long before lang servers existed.
When I want to look a function signature up, well there's two ways I do it.
1) vim + ctags and Ctrl-] will take you there (usually, sometimes it gets confused).
2) grep (or nowdays, ripgrep) the codebase for the name of the function (in another window).
As for remembering what is where. Good organization helps. After time I tend to develop a mental model of what is where and I'll just find myself popping over to the other terminal and opening a file in vim to find what I need.
You can take syntax highlighting from my cold dead hands, though.
Using a concise language and style helps, too.
It's nice, but using non-shitty libraries with good documentation is nicer.
Language servers:
Bloat.
Copilot:
Why bother with AI when I'm good at writing buggy code myself?
Remembering types and fields:
Don't trust memory. Write documentation—if only for yourself if not for your colleagues.
The language server go-to-definition feature:
That doesn't require a language server, just a cross-reference; see also ctags, bloat (above).
I'm on the fence about copilot. It's like today's story of Julius, an assistant who is beloved by management, but whose work demands attention and must be corrected. Doing a Google search for something is less "efficient" but I need to take frequent little mental and physical breaks anyway.
For other languages - typing is never the limiting factor, and actually reading the docs is better than praying that your IDE gives you something meaningful. For languages with a working REPL (or even a typo-detector) you can use that if you really need to introspect.
These days of course computers have no issues. However, I've moved to a completely Docker Compose driven workflow, but for various reasons, I've run into issues getting LSPs etc working correctly. From time to time I'll take a stab at getting it working again, with some success, but rarely do I have that kind of free time (I typically work on small teams)
I definitely recommend IDEs for navigating large codebases at work, but I feel like autocomplete has gotten much worse over the past ten years, as it tends to be slower and less concise.
Now get off my lawn.
Now I do C++ and I would fight to the death to keep autocomplete.
But Copilot I bought a license for a got a refund for after a few months. In the net, it wastes your time.
And I mean, copilot in particular is especially bad. It refuses lots of valid programming questions. Other LLMs are better. But they still are wrong a lot.
Copilot really helps me as "inline ruby/rails docs". I think the RoR docs are pretty bad and many gems are even worse. Rspec docs are incredibly unclear so I rely on copilot + chatgpt and other files to find out how to do things hah. Usually I reword the question a few times to find multiple solutions and I pick the one I like best.
I am looking for a terminal/offline LLM I can use for templating tasks, as most other programming mistakes are caught by my linters and tests anyways.
I usually only have like a few hard questions per day, where I have lots of unknown unknowns.
If the LLM doesn’t get me an answer within the free tier number of messages, it’s not gonna get there
I use JavaScript and Python. Because they are dynamically typed, I never had full confidence in IDE-like tools in my text editors. I see those languages as flimsy foundations to add IDE tooling to, I would always experience situations where the editor had no idea what type something was. So I stopped bothering entirely.
Typing the whole thing, while having manuals open for quick cross-checking.
Using text search tools to find code.
Like the old saying, there is only 1 COBOL Program, all others were copied/pasted from the original.
But yeah, oh my is vscode noisy!
Autocomplete mostly pisses me off. Most of my completions are triggered accidently. Whether it's because of me, the trackpad, or my crappy keyboard, I dunno.
I want the suggestions off to the side, not in some rando pop-over that's always moving around. So distracting.
My alternate UX notion is for the cursor's focus to always remain in the middle of the editing window. The content (source code) would scroll up and down "under" the cursor.
Then all those inlines, suggestions, autocompletions, popovers, whatever could always appear in the same location. Not dance around or try to fit where there's no room.
Next, since I'm polyglot, I spend a lot of time splunking javadoc (or equiv). I want some kind of hybrid between suggestions and abbreviated javadocs.
--
There is one onerous (to me) task which chatgpt excels. My new tool injests SQL. I'm assembling a corpus of tests, scrapped and copied from all over. Whenever my parser fails, chatgpt helps me to identify the correct dialect (grammar specification BNF) to reference.
For straight coding, I mostly use chatgpt to get oriented, get started. For common tasks I haven't done (any time recently). Like how do today's yoots do command line parsing? Replacing stackoverflow, more or less.
Otherwise, my current (hobby) project is a new take on an old problem. (Re)Invention and innovation. Chatgpt is sometimes helpful (to me) for brainstorming or validating a notion. But I'm too ignorant to know how to get chatgpt to help me create a new (to the world) thing.
If I was doing CRUD or game development, I'm sure chatgpt would be very useful.
I use ag (silver searcher) to instantly match any usage across the project - frontend, backed, SQL strings, whatever! Also since it's CLI, I can pipe/grep to refine the results even better.
Or alternatively I just make the change I want, hit compile, and get a nice list of all the breakages.
The language is way more important to me than the "tooling". Especially when certain features can oppose each other:
Example 1: null detection. Your IDE prides itself on finding nulls, when really your language designer was the one that put them there. Most langs don't allow reading uninitialised data, so if you accidentally read a null, it's because someone deliberately wrote a null. Far easier to not have nulls, than to insert them and take them out later.
Example 2: IDE type-overlays. If I write 'User user', my IDE scolds me and tells me to write 'var user' instead. Then the IDE has the audacity to insert a small grey 'User' back into my text because it knows 'var' isn't helpful.
The new "intelligent" autocomplete is annoying AF.
In some cases, it's great. Gives me a suggested function call that exactly meets my needs.
In other cases, not so much.
Here's a [hypothetical, but not really] example:
Say that I declare a property, like so:
Then, later on, I want to reference that property in a function: When I start writing the if line, autocomplete invariably suggests something like: Where the heck did that come from?But wait! That's not all!
If I later reference the function call, autocomplete often does this:
WTF???It will often hallucinate variants of API calls, as well. I have to be careful, when adding delegate functions, because they are completely legit, as far as the compiler and linker go, but will never be called, because their signature is wrong.
Perhaps if you are working on things in a very "scatter gun" way - i.e. you write a function here a function there but never really go deep in a codebase to really grok it then you'll never really get an understanding of it.
In the old days - or when LSP just doesn't work which for me is probably more than 50% of the time it seems - I rely/relied on compiler errors and/or test failures to detect problems. You can just open up the file that contains the function you are calling etc to go read what it expects.
FWIW I use all the fancy bells and whistles in terms of autocomplete and LSPs when I can (and when it works it is a great help) but I never use AI directly in the code-writing-flow. For me this is akin to the bad old days of junior/offshore Devs just copy-pasting code they found online/on stackoverflow and trying to write glue code to connect it all up. You end up with a real mishmash of inconsistent approaches/styles, duplicated code that does the same thing but in different ways, code that kinda-but-not-quite does what it needs etc, usually no coherent tests, etc etc - tl;dr super low quality and hard to maintain mess.
I will certainly use AI to help me with a few things, perhaps provide an example way of doing something/inspiration, but it is more akin to rubber-ducking and the output of an AI is never directly used. I view it as a quicker way of going to look up the right algorithm in a text book or something.
I don't think the way I work is all that different from the way people who use a lot of IDE-like features work. The main difference is that I prefer a pull-based approach to getting information, rather than having my editor push information at me or try to do things for me.
My typical setup for a project is to have my editor (emacs) open with code, and separately to have my project open in a REPL (ghci). For smaller projects I use haskell-mode, which lets me open a REPL with the file I'm working on loaded automatically. At work our codebase is a bit too big and our build system a bit too complicated for haskell-mode to be able to manage my repl, so I use a separate tmux buffer to open ghci.
I can do a lot from my repl. If I want to know the type of something, I can either look it up using the repl if it's a top-level binding, or I can add a type hole and reload the file if it's something that's not directly available in my repl. I can also see what instances are defined for a type, what functions are available in those instances, and where the type is defined.
In addition to my repl, I'll usually have a shell open in my project. If I'm trying to figure out where something is defined, I can just run a command to query hiedb, although in reality it's often fast enough to just use ripgrep.
If I'm trying to remember the name of a file or where it lives, I will typically use fzf, either in my shell or in emacs. I can usually remember enough about the name to at least narrow it down to a couple of options.
For things that live outside of my project, I wrote rofi-hoogle (https://github.com/rebeccaskinner/rofi-hoogle/) to let me search packages and documentation from my desktop with a simple keyboard shortcut. It also lets me jump to the online documentation if I need to read more than the type.
I don't use copiolot or any other sort of AI auto-complete at all. Occasionally I'll present a simplified version of a problem to ChatGPT and ask it to write code, or I'll ask it to review some code, but I typically do that through the web UI. I keep intending to get it set up in emacs but I haven't bothered yet.
I wouldn't necessarily say that other people should adopt my way of working, but it's the best way of working that I've found for me and my own quirks. I've tried IDEs every so often, but I find that the constant distractions make it hard for me to focus on the task at hand- whether it's things popping up in my field of view, auto-complete moving my cursor around, or error messages popping up because I haven't finished the code I'm writing. I'm probably slower at the IDE-specific tasks than people who are really good with IDEs, and maybe even slower at those specific tasks than I would be using an IDE, but the benefit of being able to eliminate distractions and focus on the code I want to write outweighs the cost for me.
So when someone says he do not need an IDE because its feature are just distraction, my first question is always about the biggest project that he has worked with, and the second one is how he do rename/navigation/... with his code base. Never find a single guy does not fall out of these 3 groups.
Copilot is another thing for me. The reason is its accuracy is not good enough yet. The moment it can produce code align with my intention 100% accuracy, just like how search/refactor/suggestion that IDEs are currently offer, I will include it in my workflow instantly.
For the longest time I've only used a dumb variant of autocomplete that is scoped to only suggest names found in files that were loaded in the current editor session. It's not context aware like an LSP would be. It may sound bizarre, but I never really felt that I missed much. C-P/C-N, scroll one or two items away and most times I get what I want. If I needed to use an unfamiliar feature, I'd open my past notes or the docs and skim through them, or I'd reach for the REPL. But working like this, I think you also develop some extra awareness and other tacit optimizations that reduce the need to do this sort of things often. You remember many small details, you develop a knack for taking notes, you get efficient at using external tools, you learn to quickly access docs from REPLs, that sort of things. Someone else mentioned code organization in a comment. That's definitely a big deal.
I've often been fascinated by the approach of transforming the editor to integrate all the things for which I have separate tools. So I decided to try it out for a few months (Neovim has some powerful plugins). I ended up keeping some of the suggestions, mostly the ones that aid in navigating the file system within the editor and finding files (e.g. Telescope). But I think my LSP experience has been one of the most distracting programming experience I've ever had. Perhaps if I'd given it more time, I might have gotten use to it. But it felt less productive for really marginal gains.
I think describing my experience can bring to light that some of us prefer working depth-first, while others are comfortable breadth-first.
The "real-time" nature of the warnings and suggestions was inappropriate for me. It felt like trying to write an essay and being told to worry about form and grammar, while I'm still developing my thoughts. Laying down the foundations of what I'm trying to express and meanwhile things popping up in my face completely out of context. By context here I mean at the wrong time. I'm currently laying down the structure of my thoughts and I see the editor issuing warnings about syntax or wrong imports. Please, this is not the time. Let me think. Stop distracting me. I'm ok to not have it right on the first try. When I run the damn thing, you can complain all you want. Don't worry, I'll understand what you want and will fix it. Right now I need to think. This is not grammar time.
The fix for the above was to configure the LSP to be mostly silent and on-demand. But then there wasn't much difference to using it externally to the editor.
Give it a few weeks (or even a few months, I don't know). You quickly learn the "rhythm" of codebases -- and then eventually you've seen it all to the finest details.
Then it's "ezmode." And it's not just for one codebase, you become better and better at realizing the way a code base is architected (code base being anything from an actual application, to an SDK, etc).
Many still use rg and fd or fzf outside or within their editor and are anti IDE.
I think ultimately they do waste time, particularly they see refactoring as a big 4 hours job when people using intelliJ and other proficient IDE will see it as a 5' job.
That said, have you every felt, that using all the bells and whistle, half the time you're being impaired by them? Auto-complete nags you, copilot is completely off the plot and spewing tons of stuff all the time.
The key, I think, is to have all the bells and whistle but have shortcuts to turn them on/off. I have auto-complete only drop down on ctrl+space shortcut, not automatically. And for copilot, I have ctrl+cmd+c for turning it on or off.
That way I can free flow type code as I think of it and get the tools to assist whenever needed. It's the best of both world. The IDE as clutter free as possible, and all the tools at a finger tip.
But renaming symbols throughout large projects without an LSP? Nah, been there, done that, for decades, and I'm glad that's over.
There is a trick -- never renaming anything. If the name is minted, that's it. It's canon.
I use Neovim with 0 plugins (apart from themes). I code in Go, and frequently reference https://pkg.go.dev.
When I do need to code in VS Code (rarely), I find that the autocomplete just gets in the way of my though process. It's like having a train of though barreling along and then suddenly an elephant gets on.
Also, I didn't even know what an language server was until looking it up after reading this post.
About Copilot: I have many problems with AI. They guzzle electricity, tend to be run by corrupt corporations, and are filling the internet with BS.
If I need to look up the definition/implementation? `:vsplit` works fine. If it's on the internet? https://pkg.go.dev.
If you don't trust me, you can check my dotfiles: https://codeberg.org/Kaamkiya/dotfiles
This means, that most of the time I deal with a system after it was deployed, often times in a distributed system with most of it only accessible remotely. So, the aspects of my tools that I value (over autocompletion) are integration with shell / terminal, integration with debuggers / tracers / profilers, ability to quickly search / patch files in any format.
Typically, if you look at my monitor (when I'm actually doing something), you'll see Emacs running ansi-term with tmux that shows a bunch of buffers with shells in VMs in different corners of the world, database interactive clients, interactive debuggers, interactive interpreters, vi with multiple tabs open, multiple journalctl -fu xxx buffers, or perhaps kubectl logs -f xxx buffers etc. and outside of tmux some Org buffers with lists of tasks or text that I use to export to JIRA markdown.
(I think my setup would make for an exciting hacker representation in some sci-fi movie! It's all green letters changing rapidly on black background in many small adjacent boxes)
Even in this context, there's some word completion available (eg. in shell, or in Emacs just trying to complete the word by finding similar words in the same buffer), but usually by the time I actually need to write something new (rather than add breakpoints or investigate in some other way), it's crystal clear what needs to be written, and I don't need to leaf through pages of autocompletion to find the right word.
---
Now, while most developers don't spend that much time figuring out why their software doesn't work, I still think it's a significant portion of any developer's work, so, simply from the standpoint of how much time you spend in what environment it might make sense to go for a tool that's better at editing text than a tool that has better integration with LSP.
Grep, etc to find shit.
Then I dunno, it just kind of works.
This was back when Windows was finally getting good (Win95) and I was not familiar with Unix at all. All of this seemed like the obvious progression from text base UI's to GUI's.
So for me it has been very natural to use autocomplete and expect to just attach a debugger and inspect running code and even modify it (VB allowed edit and continue) which is still a rare thing today that I miss.
Also using a mouse to move around the code base and inspect it just like using a GUI to use the computer. Because of this I have never developed a strong keyboard only ability, I expect a mouse to be available.
I remember getting into web development and having to step backwards into a language that wasn't really typed and no real IDE's that could autocomplete an interactive debugger, back to print debugging and text searching. Eventually the browser developer tools caught up but the JS language was pretty difficult to accurately autocomplete and navigate / refactor properly at a code level with an IDE.
Was fun to watch all the JS devs get excited over Typescript because autocomplete and refactor now worked reliably. For me having a language with real types the IDE can understand was the way it's supposed to be.
It seems like it's how you got started that determines your preference. If you were a Unix guy you liked the command line and TUI's and everything was about wrangling things at a text level, everything is a file and just a stream of bytes. If you were a Windows or Mac person then it was GUI's and mouse interaction and it was more about widgets and objects and IDE's.
I can appreciate both sides, I would like to be stronger in the command line and keyboard but how I got started strong influences the tooling I prefer and I am sort of set in my ways now, I suspect that is pretty typical.
If you can, then do you feel helped by automatic transmission? I don't. I feel like too often the automatic transmission makes the wrong choice. It doesn't feel like more work at all, and allows for much better control of engine braking.
(You shouldn't be using your other hand to hold anything while driving anyway)
Manual transmissions get a bit stressful on steep hills, and so I don’t really miss them much accordingly.
I guess I keep everything in my head and use reference materials when I forget something.
IDEs bother me. I enjoy working in the terminal and have always found using a mouse while writing code annoying. I also dislike having tabs, buttons, menus, drop-downs, pop-ups, etc., in my way while I think and write code.
Seriously though, pressing Ctrl-B in JetBrains IDEs is half my workday.
I just tried out Copilot starting a couple of weeks ago for Advent of Code, and I'm very impressed. Easy 2x speed boost, with occasional bursts of much more. I'm mostly using Java, though I've tried Perl and it knows that too.
> how do you do it? Do you just remember every type and field in a codebase? What does your flow look like?
It's very different for a codebase I'm familiar with vs one I'm still learning. I do the vast majority of my programming on the former, where I don't feel most of the features you mentioned really matter. Yeah, I remember the types and fields. Usually they're already being used several times in the same file I'm already working in, so it's rare that I even have to make an attempt to recall something, and even rarer that I fail to. When I need to change or add something, I simply open the relevant files and write the relevant code, because I know where it should go.
When I'm learning a codebase, there's a lot of jumping around and looking things up to get familiar with it. I've found that using IDE features during this stage makes me slightly more productive in the short term, but makes me much slower at becoming familiar with the codebase in its entirety (or the subsystems relevant to what I'm working on, in very large codebases). Being familiar with the codebase in its entirety is very important to me and I feel like it enables me to design features and bug fixes much more reliably. My number one priority when working on a new long term codebase is to get to the point where I can fit basically the whole codebase in my head (with a fidelity depending mostly on the size of the codebase), and I do that much better when I'm working on it raw and not outsourcing my understanding to tools. Then once I have that complete understanding, I no longer need the tools anyway.
> What do you do if you need to look up the definition/implementation of some function which is in some other file?
I open the file it's in and read the code. If I don't know where it is (which is rare, especially because most languages and codebases have rules or conventions for file/module/function names), then I grep the codebase for its definition before opening the file. But usually if I'm in the Foo subsystem and need to check the definition for its Bar.baz function, I know it'll be in src/foo/bar.ext and I can "/def baz" or whatever to jump to the definition as soon as I open the file. I've been meaning to try getting used to fzf vim plugins for the last 5 years or so but I really just don't care enough to put in the effort. It's obviously slower to grep and open and search for the def, but the problem just doesn't come up often enough for there to be a good incentive for me.
I type 'make' and let the compiler show me where the definitions fail. Often it will suggest the valid options. Opening the header in another buffer or window also helps.
I value semantic correctness first and don't get distracted by syntax errors if I'm in deep concentration. Conversely, I often make small changes when I anticipate a compiler error so I will know exactly where it came from, fix it (often by trial and error) and move on.
The first IDE I used was Zend Studio [0], because I was writing a PHP application for the IBM i Series, and Zend pushed their IDE along with their licensed server [1]. (Note that in modern times, with IBM OSS for PASE, PHP for the IBM i can be sourced from e.g. Seiden Group for free). Zend Studio is eclipse based, and terrible - their modern landing page advertises PHP 7.1 support. About a month into using it (this would be sometime circa 2018), IIRC, the whole thing broke while I was tinkering with basic appearance preferences, and the application would no longer open. Instead of fixing it, I asked my manager if we could buy a PHPStorm license, since I kept seeing it mentioned in blogs and tutorials and it sounded nicer.
PHPStorm was an absolute game-changer. It was the first time I experienced the miracle of modern language servers. Go to definition? Holy shit! It can see errors before I test the code?! IT CAN HELP ME SPOT ERRORS IN MY JAVASCRIPT?
My PHPStorm license was probably the single highest ROI expense my company ever took on my behalf. That led to all sorts of rabbit-hole learning, like setting up gulp with babel and sass and autoprefixer back when we were supporting all the way back to IE10 on our websites.
When I went back to school for a BS CS, I downloaded CLion expecting as-good support for C++ (our curriculum's language) as PHPStorm had for PHP. I was a little disappointed, but from what I hear CLion is much better now (with the Nova engine). On another note, JetBrains threatened to suspend my account when they saw me using an educational CLion license on the same account as my corporate PHPStorm license... Explained my way out of it (as I was using the licenses correctly / following EULA) but it was a sour moment, especially considering they recommended people join all of their licenses (personal & professional) under one account [2].
Regarding CoPilot: I haven't used it and probably won't, unless they add truthiness to LLMs. I've found that actually typing my code is rarely the bottleneck, and sparing myself from typing is the "value add" most coding LLMs offer.
[0]: https://www.zend.com/products/zend-studio
[1]: https://www.zend.com/products/zend-server/ibmi
[2]: https://sales.jetbrains.com/hc/en-gb/articles/208460135-Can-...
I don't remember every type and field in the codebase--I have to familiarize myself with them when I work with them, just like you do--and if you don't familiarize yourself with your types, the IDE telling you what your type is, isn't going to be particularly elucidating. With good code, it's usually pretty clear what the type of a variable or field is, even if the type definition isn't in your current viewport. If that's not the case, your code is likely overcomplicated, and needs a refactor. If there's too much inherent complexity in the problem to allow for that, then you've no choice but to keep a whiteboard handy to sketch stuff out--that's much easier and more useful than anything an IDE does to trace complexity.
Go-to-definition lets you lose track of the big picture, because you don't have to have a working knowledge of your project's file structure to work around it. The result is often multiple definitions of the same thing with loose translation layers between them which people don't really touch often enough to feel obvious pain from, so they end up just sitting there hurting performance and introducing subtle bugs whenever adjacent code changes. I've worked on a few Java/C# codebases like this.
It's really important to me to read and understand all the code around what I'm modifying before I make a modification, so I can make exactly the change I want to make and nothing more.
Workflow: Make sure you are on the same page as product with what the feature is, asking any questions you can think of. Write an integration test, if possible (it isn't always). If not, write a test case for manual testing (it's better if you can get the testers to do this, and even better if you can do it with them). Get an architecture in my head, or draw one on a whiteboard if it doesn't fit in my head. Visualise the "flow" of data through the different units, and what needs to change--if the feature is complicated, write this down so you don't have to start from scratch figuring it out tomorrow. Start at the beginning of that flow and write a unit test, then implement it, cycling through unit test and implementation in a circle until each unit change is implemented. Then run the integration test and manual test. Take a step back and see if there's anything you missed. Deliver it to product/testing.
is rarely useful to me, I just type things out.
I'm just quicker this way. By that I mean that my stream of thought just flows, when thinking about autocompletion is like in another "brain space" which makes my mind stutter.
I don't know if I'm wall-clock quicker for real, especially as I'm not that fast a typer (certainly not as fast as I want to be) but the mental exercise is annoying enough that I prefer the smooth tarmac of typing it all vs the pebbled trail of jumping back and forth between thinking about the output of typing and autocompleting.
If some autocomplete produces a dropdown list the thing it does help me is a quick feedback as to whether I made a typo, but I basically never tab the autocompletion out.
Only time I do tab out autocompletions is on the shell, and mostly only for paths.
> language servers
I do kind of see the appeal e.g VScode's invoking this inline view of a method implementation right at the call site. In practice I never use it.
Another one is inlay hints in Vim, e.g with type information for dynamic languages (e.g Ruby when using RBS) or to correctly feed autocompletion.
What I don't like is how heavyweight LSPs "feel".
> and recently copilot
I tried Copilot a few times and it was a) slow and b) mediocre.
Slow is stupendously because it feels like I'm driving an autobahn, except littered with 10kph roadbumps.
Mediocre as it sometimes produces code that works, but most of the time is either wrong or flat out doesn't work; even for boilerplate its output is bad/inconsistent with the codebase/outright incorrect. So every time I have to enter a "review mode" state of mind and fix the damn thing, which ends up being slower than just fully typing the thing out in the first place.
> go-to-definition
:sp <leader>p lib/whatevs (backed by fzf) then /thething because codebases I work on are typically organised sensibly.
Or I just open a new tab and rg thething then vim thepath +:42
Unix as IDE is stupidly fast and powerfully flexible for me.
> Do you just remember every type and field in a codebase
Mostly, not everything but I do have a mental map to find things quickly with the above.
Rereading the above, I realise that maybe the takeaway is that I'm a "fixes radios by thinking" type of person, which means huge mental models and map of everything fit in my mind, then I reason it out and come up with a solution in my mind, and I just need to type it out as a final step in a sort of stream of thought way, so anything that gets between the mind and the characters appearing on screen is a major drag.
I don't think it is any worse or better than an IDE, it's just the way I work, and I've seen some people do crazy stuff crazy fast with IDEs/LSPs/LLMs just as well. It's just not my way of operation.
For your question about go to definition: not like it takes much time to open a file and search. Yes, of course objectively it takes longer, but I don't feel like my coding speed is limited by my typing speed. (a mouse is not involved when opening a new file)
I mean to set up LSP again because it has to be better. But in the past, when it's broken or I code on a different machine I've found that I don't miss it, or even necessarily notice it being gone.
I write code. I mostly remember things but I check the documentation often.
Docs.rs is always open.
Then I compile it and fix the compiler errors. The Rust compiler errors are very nice and friendly. So it's not that difficult.
I mean that's how C and C++ programmers worked for many many years.
I usually use Python in medium-size projects and remember the types for a couple of files at least. It’s almost always one of the top five builtins or a custom class I wrote or modified. If in doubt I click on the word and it highlights everywhere. Usually obvious what it is by context.
You don’t understand the essence of your job.