Ask HN: Which tools have made you a much better programmer?
Getting better at coding is usually a long, slow process of study and practice. However, sometimes I run into something that's easy to understand and, once I'm using it, feels like I've leveled up.
A few personal examples are: * version control - specifically, reading up on git and understanding the more complex commands * debuggers * flame graphs for performance debugging * good code search
What have you added to your workflow that's made you much more productive?
519 comments
[ 2.9 ms ] story [ 346 ms ] threadI use https://www.spacemacs.org/, but modal editing is a win.
Nowadays I'm using VSCode because it has many many features not available on vim (or available with plugins with a large learning curve that I can't have right now) but I always use vim mode. I can't imagine not being able to navigate like vim and have now added vim-like bindings/commands to some other programs like Firefox.
basically, whatever allows increased rate of I/O between you and a computer.
it's really as simple as never leaving the home row and im pretty close to thinking that the mouse as an input device has held people back.
When you’re on it, also install Vimac [1]. I’ve mapped it to ⌃Space (similar to Spotlight’s ⌘Space). It’ll make you grab the mouse / trackpad less often.
[1] https://github.com/dexterleng/vimac/
As a junior developer, method documentation and autocomplete on demand are crutches i leaned on VERY heavily with great success.
- Unit testing
- Leak detectors (for languages like C/C++)
- Race detectors
- Auto-formatting (eslint, gofmt)
- A solid editor (prev: vim, now: vscode)
- One-step deployment scripts/automation (even for small projects)
I have not found a lot of value in debuggers, outside of after-the-fact debugging (e.g., core files.) I much prefer printf-style debugging.
The script makes sure changes are tagged, committed, and pushed, tests pass, builds and pushes container images, static files, and then rolls out binaries. The rollouts depend on the infrastructure -- it could be something like scp + kill/restart, or a kubectl command, or "gcloud run deploy", etc.
One-step deployments vastly improve the quality of the service because I can test things in real environments quickly, push out patches and bug fixes quickly, keep updates small and bounded, and bonus, I can come back to a codebase months later and not have to re-learn how to roll something out.
Initially I was thinking into having a CI build the images and push it to the server and so one but for my personal projects, it's too much. So I might just do a bash script to build the image, push it to the hub and then connect to ssh to pull the image and restart.
Regarding your script, could you share a template?
Have you heard of/tried rr? I think you might find that it fits your thought process better than traditional debuggers.
https://rr-project.org/
I hate I have to do these stupid exercises to be able to get a job, but I have learned some useful skills by looking at how I write code and comparing to faster solutions. It has changed how I write normal code, and helps me write automatically without thinking too much.
* `scan-build`
* https://github.com/pwndbg/pwndbg, a very comprehensive gdb extension/config.
- intellij, or any good ide. seriously, we underappreciate everything these tools do and can do for us.
https://en.wikipedia.org/wiki/Rr_(debugging)
- Unit testing. No true developer can call themselves such until they can programmatically verify that their code works to some explicit specification.
There's tons of other things that are bonus points. But I wouldn't call anyone a true developer until they understand and use both of those.
The more integrated it is (with your IDE/editor) the better the experience and productivity boost.
And the difference is quite large. When you are working with a language that has first class REPL support you start to
- 'get' why Emacs exists
- become faster at writing code in general
- write much more experimental and open
- become more motivated in testing smaller assumptions and asking questions about your or other peoples code
With "first class support" there are three dimensions:
(1) The REPL and the editor/IDE have to be understand each other well.
(2) The language itself has to be malleable and (de-)compose well in terms of syntax and idioms.
(3) many things in the language are first class expressions or in other words: there is a high degree of tangibility in the language constructs.
Most dynamic languages have workable/decent support for REPL driven development so it is always worth testing out.
You find excellent support in: Clojure (and of course other Lisps) and Julia from my experience.
- ClojureScript with Figwheel and the web browser
- Clojure with Emacs Cider, Clojure with Cursive
- R and Rstudio
- Matlab
- ipython jupyter notebook
- Pycharm debug breakpoints that are triggered by unittests (Running the unittest to initiate a python repl at the breakpoint)
However, one thing I've found invaluable over the years is developing operational tools to support my deployed code, in a language with a built in REPL.
At different times, I have used Clojure and Jython for this (high level way to call my Java libraries, or to invoke APIs over the network), and most recently Ruby (has been easy to deploy and run scripts or ad hoc commands over irb for operational tasks, in the same environments where my service runs).
This allows me to build up code over time that I can use to
* Quickly make calls to my service to triage or debug production issues.
* Write scripts to quickly validate a new install.
* Script operational tasks at a high level that doesn't make sense to build into the service itself (can allow the service to be more stateless, for example).
* Bypass layers and make calls to the underlying database (can be more powerful than the command line tools dedicated to a specific database).
* Can be more powerful and composable than curl or Postman for making web calls.
* Have used it to analyze the state of messages in a Kafka topic (with a custom gem).
So I highly recommend building a tool set around a language with a good REPL for anyone responsible for a service with a REST API, or any other kind of API available over a network.
I also often use the REPL as a "tool" rather than just a editor feature.
For example this week I'm working on a data integration. This is a very specific one-time task, so there is currently no need to write accessible production code. I can just use the REPL to do the "ETL" and leave the code as-is.
There is merit in keeping it and find functionality to extract, abstractions/compositions in the future, but the point is that the integrated (Clojure) REPL itself is already sufficient, powerful and very ergonomic tool.
I have trouble understanding professional challenges of people that never had a chance to learn those. Like accountants struggling to merge tables or proposal writers painfully tuning document layouts.
You can do so much automation and data munging so quickly with it that it really makes you feel like you have superpowers.
https://github.com/hlissner/doom-emacs
https://www.gnu.org/software/emacs/
After years of dealing with a Windows workstation, I went into credit card debt to purchase a quality laptop computer for my development needs.
I have had the same one for 6 years and found little reason to buy a new MBP.
Windows is usable even for node and git development nowadays.
The only thing that doesn't work in mine is the fingerprint reader.
Homebrew (package manager). A terminal that speaks an actual standard (no weird CMD.exe, no PowerShell.) gcc built in.
Note: All these benefits are just benefits garnered by using a system with a unix base. I should've just committed and installed a dual-bootable Linux partition on my Windows machine a long time ago.
Unfortunately I have a hard time explaining exactly why developing on Windows is such a pain... The best success I've had would be going through Git Bash which always felt like a hack, and that's just for nodejs, if I want to compile a C/C++ program the whole thing is a nightmare with Visual Studio. I think OSs should be judged on 'time to compile a C/C++ program' from a fresh install.
Hell, I think 'the tool that makes me a much better programmer' is being terminal-native. Get used to unix systems, they're wonderful.
/rant
Using Golang has helped me create better data structures, and using C helped me understand linking, using python helped me understand closures, and using Ruby helped me understand that I hate programming.
I don't like programming. I like it when the computer does what I tell it to do. I find that drastically easier to accomplish in Ruby (especially Rails).
See: https://crystal-lang.org/
We have ways of dealing with scaling of runtime in production (develop infrastructure environment).
There's no way I know to speed up the edit/compile/run loop during development.
- bash + GNU coreutils; seriously, take the time to be able to write helpful bash scripts which can run basically anywhere.
- git; use it even when you're not pushing to a remote. Add helpful aliases for everyday commands. Build a good mental model of commits and branches to help you through tough merges. ( my ~/.gitconfig: https://gist.github.com/jeaye/950300ff8120950814879a46b796b3... )
- Regex; combined with bash and your GNU tools, a lot can be done.
- Vim; modal editing and vi-like navigation can blow open your mind. Explore the existing plugins to accomplish everything you want to be able to do. It's all there. ( my ~/.vimrc: https://github.com/jeaye/vimrc )
- Functional programming; if you're new to this, start with Clojure, not one of the less-practical languages. This has made such a huge impact on the way I think about code, write code, and design code that it's possibly the biggest one here for me.
It lets you modify and save code from the quickfix buffer, so when you search for something and it shows up in the qf, you can do a find replace / edit / etc. This is especially great for mass refactoring / renaming.
Jupyter Notebook: https://github.com/lambdalisue/jupyter-vim-binding Jupyter Lab: https://github.com/jwkvam/jupyterlab-vim
I've been toting around the same .vimrc for like 6 or 7 years and there are so many better plugins now.
Vim has been probably the most profitable tool I've ever picked up. Or maybe git. But I think Vim.
1. Stop creating counters/loops and become facile with map, reduce, and the like. This will shift your thinking away from blocks and toward functions.
2. Take the time to really understand what side effects are, and start avoiding them everywhere they are not necessary. Keep scopes as local as is practical.
3. When you start toying with functional programming per se, make sure you really have your head around recursion. That's where much of the magic concision comes from.
xargs is analogous to map() in this situation, and the script needs to have limited side effects to work well with concurrency. xargs -P4 for example.
But then you don't get any of the newer stuff.
But at least make sure that your language supports closures.
Disagree. This is likely to 'dilute' the lessons of functional programming, as it were. If you learn to program in idiomatic Clojure/OCaml/Haskell/Scheme, you can be relatively sure you really have picked up the principles of functional programming.
If you merely attempt to apply new principles in an environment you're already comfortable in, things aren't going to 'click' in the same way.
Beside that, plenty of languages simply lack the facilities needed to effectively use the concepts of FP, as vmchale says.
Other unique French US cultures:
Haitian Creole
New England French were are Canadian migrants.
Missouri French
Muskrat French from Michigan
North Dakoka Metis French
It could have come to some from 40 years old but it was already here for many.
Still it's a French word. It is like going to Germany and learning an English word but calling it German.
(edit: spelling)
For a simple example, val newList list.map(function Call(_))
Instead of val1 = functionCall(1) valN = functionCall(N)
The ideas themselves are not particularly complicated, but I hadn't previously worked with abstractions where the default was to operate on whole data structures rather than on individual elements, so I didn't see how you would set up your program to make those functions useful. In addition, for abstract higher-order functions, type signatures help a lot for understanding how the function operates. I found `functools.reduce(function, iterable, initializer)` significantly more opaque than `foldl :: (b -> a -> b) -> b -> [a] -> b` because the type signature makes it clear what sort of functions are suitable for use as the first argument.
It's now easy for me to use the same abstractions in any language that provides it because I only have to learn the particular encoding of this very general idea. While I couldn't figure out why functools.reduce was useful or desirable, I couldn't figure out many parts of C++'s standard template library at all. But if you already know the core concepts and the general way that C++ uses iterators and the fact that functools.reduce, Data.Foldable.foldl, and std::accumulate[4] are all basically doing the same thing for the same reasons is a lot more readily apparent.
[0] https://docs.python.org/3/library/functools.html#functools.r...
[1] https://docs.python.org/3/library/itertools.html#itertools.s...
[2] https://hackage.haskell.org/package/base-4.14.0.0/docs/Data-...
[3] https://hackage.haskell.org/package/base-4.14.0.0/docs/Data-...
[4] https://en.cppreference.com/w/cpp/algorithm/accumulate
That's a good point. Using a proper functional programming language doesn't just enable FP ideas (you can't fake a feature like implicit capture of variables), it may also clarify them by reducing baggage.
> I found `functools.reduce(function, iterable, initializer)` significantly more opaque than `foldl :: (b -> a -> b) -> b -> [a] -> b` because the type signature makes it clear what sort of functions are suitable for use as the first argument.
I suspect you're just a better Haskell programmer than me (I've only ever dabbled), but I find the big-mess-of-arrows syntax to be pretty confusing compared to a simple tuple of descriptively named identifiers.
Perhaps related to this: I don't see the practical appeal of currying. Even C++ supports the 'bind' pattern just fine - http://www.cplusplus.com/reference/functional/bind/#example
In non-FP languages, I didn't originally appreciate the benefits of the pattern. It was more work to do things functionally, so I dismissed some patterns that were actually useful.
I'm a bit biased, but would recommend Elixir as an accessible FP language. It has an accessible syntax and modern tooling.
You may be frustrated with the constraints of immutability for a few weeks, but the benefits become apparent once you're used to it.
Now when I work in non-FP languages like JavaScript, I will apply FP principles when it makes sense.
I then moved to FP-TS, which makes more heavy use of haskell-like patterns and monads.
The hard part isn't the syntax of whatever language, but understanding the new patterns and way of thinking, which you can do with a simulation layer like FP-TS (for typescript/javascript).
The functional patterns and emphasis on types makes your code robust and more correct. It emphasizes correctness, which is ultimately what your job is as a programmer. Optimization comes after.
Though other people at my work dislike it due to them not understanding what's going on
There's a lot of resistance to adopting anything with the name functional, and that resistance is often seen in a /refusal/ to try to understand instead of a mere lack of understanding: people put up walls straightaway. I expect many need motivating examples to guide them to it.
Languages like Kotlin are pointing the way towards that middle ground far more effectively than, say, Scala did.
I would want to program in functional too, but I would seek out projects or teams that already use functional. I’d never introduce a functional language to an already established team or project, unless of course, I was the CTO and there were clear benefits.
Agreed that functional patterns can be hard to understand if you don't know the patterns, but if you do know them they are much easier to understand and reason about the code. It's a long term investment, and one that frankly I believe will be inevitable as more and more people start doing programming work.
Later I read a couple of chapter of SICP and then I really changed and my programming hasn't been the same since. The language I use at work is JavaScript and while SICP isn't for JavaScript, nothing else has changed my JavaScript for the better to that degree.
There's a port. https://sicp.comp.nus.edu.sg/
I am not very comfortable with this. How can I learn to do this in traditionally non-FP languages like Java? (Am CS undergrad student)
https://developer.ibm.com/technologies/java/series/java-8-id...
List of articles relating to idiomatic Java 8 code. Some of these touch on using lambdas and functional idioms.
https://developer.ibm.com/articles/j-java8idioms3/
This one shows a few of the functional-styled methods that can be used (foreach, takewhile, iterate, etc.).
https://developer.ibm.com/articles/j-java8idioms2/
Shows the collection pipeline pattern.
I have experience with the same things in C# and other languages, the way they're using them in these articles are what I'd expect from a comparable API.
MAP: Take a collection, say a list/array or a dictionary/hash, and perform some function on each member of the collection, returning a new collection who's members are the return values for each original member. It's a loop, but no loop!
REDUCE: Do the same thing as map, but carry along an output variable, and have your function's output for each member (potentially) mutate that output variable. Summing is a basic example.
I'm not specifically recommending preferring this in Java as a step towards functional programming. It's in, uh, more terse languages like Python and Ruby where the payoff is obvious [1][2]. And among not-functional programming languages, it's not just dynamic languages, either. Consider Dart (and seriously, consider Dart) [3]. Also, Javascript, which has had many features shoehorned-in over the years, has these and related functions.
[0] https://www.java67.com/2016/09/map-reduce-example-java8.html
[1] Double some numbers Python: result = map(lambda x: x + x, array_of_numbers)
[2] In Ruby: result = array_of_numbers.map{|x| x + x}
[3] In Dart: result = arrayOfNumbers.map((x) => x + x).toList();
My school (Macalester College) recently started introducing some fp constructs/concepts in our intro class such as map, reduce like you mentioned.
It was long after I took it and now I am TA-ing. Oddly enough, I am more comfortable approaching this style in Kotlin.
[alias] dfif = diff idff = diff grpe = grep
I have often wondered why that functionality disappeared, and why no one has tried to resurrect it. Search engines offer corrections all the time; why doesn't bash?
Teitelman's Interlisp
Has a DWIM feature that's
Really a screw;
MACLISP has evident
Superiority
Letting its customer
Mean what he do.
--The Great Quux (Guy L. Steele)
This poem indicates the frustrations that hackers had with DWIM at the time, which may explain why no one tried to resurrect it. Too-clever-by-half features intended to help tend to drive people nuts, especially when they fail. Even when they succeed, they interrupt the user's flow and become like that dialog box Windows users just dismiss.
gc = git checkout
gs = git status
etc...
Also not afraid to add multi-letter aliases if I find myself typing the same multi-word command over and over.
For example git diff master HEAD becomes gdmh
alias _up='sudo apt update -y && sudo apt upgrade -y'
And then define one and two letter aliases for the things you do often:
st=status
l=log --with-prettiness
ap=add --patch
shit=reset
co=checkout
Honourable mention also to FZF [1] which not only makes it trivial to locate a file in a directory tree, but has revolutionised my history use with its fuzzy matching.
[0] - https://github.com/BurntSushi/ripgrep
[1] - https://github.com/junegunn/fzf
Command ' grep' not found, but there are 17 similar ones. Maybe I'm not the only one :).
doesn't expose you to typed functional programming (the ML school) though.
If you haven't learned to touch type (it happens, I didn't learn until I was 22), then first learn that, then learn vim.
FYI: Remap your capslock key to escape to use Vim more effectively.
https://stackoverflow.com/questions/28444740/how-to-use-vi-m...
If you are using zsh, you need to add this to ur .zshrc
<Esc> or CTRL-[ End insert or Replace mode, go back to Normal mode. Finish abbreviation.
CTRL-C Quit insert mode, go back to Normal mode. Do not check for abbreviations. Does not trigger the InsertLeave autocommand event.
That's more one way of doing things rather than a "FYI". Eg I switched capslock with ctrl. There are many ways to exit insert mode, I prefer:
inoremap <esc> <nop> as this helps you train your fingers to stop using <esc>
If you're really curious, just learn to touch type and find out. It doesn't take all that long if you're already a solid typist. I'd be fascinated to read an article from someone who learned how to touch type and thought it was a waste of time.
I installed it a couple years ago, went whole hog, down the rabbit hole, but realized a couple of things.
1. I rarely ever use anything more than a simple L/R split.
2. When I do use something more complex, it's almost always in the terminal, in which case why not use tmux?
These days I'm back to using gnome because ubuntu switched to it over unity (which had a weird multitouch bug that drove me crazy).
What do you get out of i3 beyond a simple L/R split supported by simpler wms and how often do you use it?
I sometimes do move to more of a tmux split workflow, especially if I'm working on remote machines, but it's just much nicer to have the same keyboard commands for all of my windows.
Currently I use i3 with packages pulled in from XFCE to handle sessions and power management, plus xfce4-appfinder and xfce4-panel (started and killed with MOD keys of course) because I wanted something beyond d-menu / b-menu.
It all works very well and was easy to configure.
I treat each workspace as dedicated for a specific purpose - Dev, Browsing, Chat, etc. That gives me quick mnemonics to hop to each space: MOD+1, MOD+2, MOD+3, ...
Within my Dev workspace, I use a tabbed pane for top-level organization: browsers (stacked), IDE, terminal, Emacs (magit + org), etc. This keeps my focus on that space when doing dev, and away from the laptop monitor, which is only occasionally useful as a reference.
I'll occasionally stack a terminal beneath my IDE if the current task requires it, e.g. to test a deployment or a project task.
(Sidenote: is there any sort of linux libvte-based terminal emulator that has tmux integration a-la iTerm2? For when I do use i3, it would be really nice if I could spawn a new terminal on a remote server, attaching through an existing tmux session.)
1: https://unix.stackexchange.com/questions/189805/what-termina...
This would not be possibly if I wasn't using 4k monitors. That was a big shift for me, because now I think of each 4k monitor as 4 1080p displays.
I use i3 with polybar and have dedicated workspace icons (web browser, terminal, editor, to-do, email, music, etc) for quick navigation between different applications. Over time I’ve built up muscle memory (i.e $Mod+3 will bring me to my editor) that has significantly sped up my development process. While you could use another window manager for a similar purpose, I find the relatively minimalist approach of i3 + polybar in my case to be fast and highly configurable.
Us the toggle an i3 concept? I’m interested in it, can you give me the function name so I can look up doco? :)
It means I can have an overview of a bunch of different things and keep terminals context-specific (1 terminal for htop, 1 for docker, 1 for whatever remote test environment, 1 for project A, 1 for project B, 1 for some other remote host I need for some reason, etc.) If I want to do a new task unrelated to anything I'm doing before, I don't need to break the context of an existing terminal, I just press Alt+Enter, it's automatically slotted into a place where it's completely visible and usable and I can do that task quickly. When I'm done, I can close it, again, without disturbing the context of all the other terminals. It's just incredibly freeing to have that and I feel it frees a lot of cognitive load by being able to go back to a terminal for a certain task and immediately see exactly where I was and what I did last.
Also, much like the other comments, I use task-specific virtual desktops all the time. First desktop is for all the terminals. Second is for browser/communication. Third is for project A. Fourth is documentation related to projA. Fifth is projB. Sixth can be more documentation. I often have 10 virtual desktops for different things. I don't want to imagine what it'd look like if I had it all on one desktop.
https://stackoverflow.com/questions/5120399/setting-windows-...
https://stackoverflow.com/questions/45411844/before-diving-i...
1. Workspaces. Not at all unique to i3 but I’ve kept mine themed and automatically load certain apps into the same workspaces—all things i3 makes easy to do.
2. Floating scratchpad for media player. Nice to have my music controls always accessible but only visible when I unhide them.
3. Vertical split beneath my editor with a terminal. Just my personal preference, but I typically have L/R with code/browser and then split the code half vertically.
Being able to move windows across displays and workspaces quickly are other pluses, but again, not at all i3 exclusive.
https://regexone.com/
The reason I end up ditching Vim after a few weeks every time I try it (4 serious attempts now) and go back to IntelliJ (which I’ve used for two decades) is that I never found a solution to the following trivial issue:
Imagine you have a large Java codebase and you want to refactor all occurrences of a method called “doFoo()” to give it a better name - how do you do this in Vim?
This is a single keypress in IntelliJ and I use this function very frequently but I never found a way to do it in Vim.
Note: I only want to change THIS doFoo() method, not the hundred other doFoo() methods in the codebase.
Also note: yea, this includes all implementations of the interface, all abstract classes, all classes that extend a a class that implements the interface and all other funky polymorphic things, and NO unrelated code. And do it all in one keypress, don’t have me manually go through line by line.
Any ideas if this is possible now?
That being said the first thing I do after installing IntelliJ is open the plugin settings and install IdeaVim.
I use Emacs for a tonne of stuff, and basically any other language I use. But for Java, with its deeply hierarchical codebases and general verbosity, there's just no sane way to manage that complexity smoothly without a good IDE.
(One could argue that if a language necessitates an IDE to work with it, then that's a failure of the language's DX. But thats an entirely separate discussion.)
So you get all the powerful IDE commands and the high speed of Vim commands at the same time.
It's easy to set up !
https://github.com/eclipse/eclipse.jdt.ls
https://github.com/georgewfraser/java-language-server
Are both good examples. You'll need a corresponding client like this one
https://github.com/prabirshrestha/vim-lsp
There are others, but this one is pretty good. Next release of Neovim will have one built into the editor. Frankly, it's a bit of a hassle but once you get an LSP provider set up you can get one for just about any language you're using
For almost everything else e.g. git, learning how to use the command line instead of a UI is the best way for me to learn how the tooling works.
Tools that eliminate even a little bit of cognitive load moves the point of no return a little bit farther, which means there are kinds of problems you can touch that someone else won't or can't.
I HATE using a Gem then needing to break my train of thought to pull up the official documentation to see what an API interface looks like. Ctrl+click. In and out in 15 seconds.
This is why I love these sorts of threads :)
If anyone is an emacs/vim user it's certainly worthwhile to enable similar error reporting plugins to get the same effect
LSP is decent, but I've yet to see any languages with the depth and quality of Jetbrains IDE support, by a fairly large margin. I've had to fight VSCode settings many, many times in to world of Go, but Goland "just works" for the essentials - intellisense and code navigation is just so much better. Python and Typescript are probably the best-supported in VSCode, but they still don't meet the mark. Rule applies doubly so for languages that aren't strongly typed. Breakpoint debugging for code and tests is similarly hands off.
They add all sorts of ecosystem-specific know how to make the experience smooth, e.g. Rails, Rspec in RubyMine.
I still use command line tools for every other part of my workflows, e.g. Ripgrep, git, dependency management, but I haven't found anything else that compares for coding with really excellent intellisense and code nav, other than Visual Studio proper for C#.
BTW I'm not affiliated with them but they're having a 50% discount sale right now.. iirc 25 birthday sale. They seldom give out discounts, so this is a great time to upgrade too.
https://www.jetbrains.com/lp/php-25/
What cool stuff should I look at as I am willing to be sold on this.
Maybe this could help: https://blog.jetbrains.com/idea/2015/08/experimental-zero-la...
Here was a 3rd party analysis from 2015, showing a reduction in latency when editing XML files in IntelliJ IDEA from ~70ms with large jitter to 1.7ms with small jitter: https://pavelfatin.com/typing-with-pleasure/#summary
I wonder if it's turned on by default today, 5 years later.
Some other techniques: https://medium.com/@sergio.igwt/boosting-performance-of-inte...
https://blog.jetbrains.com/idea/2016/12/intellij-idea-2017-1...
https://blog.jetbrains.com/clion/2017/01/clion-starts-2017-1...
Make sure you enable Golang modules in the settings also.
Anyways just mentioning some stuff off the top of my head I enjoy:
Debugging is a great experience.
Can find a plugin for most anything, I use the Kubernetes one for syntax completion and documentation. (alt-q I think)
Also you can create .rest files and compose http requests and trigger them right in the files, which I thought was cool.
The documentation pop up by hitting alt-q in general is pretty cool. Don’t have to run over to godoc.
Then most things you’d expect from an advanced ide. Multi line editing... jump to definitions and implementations... Project wide code search and replacement
Jetbrains UI that shows available options or pops them in real time.
And command line UI; requires reading the docs to learn options, but can be powerful and chained with other commands via pipes, ez automating UI interactions into scripts, etc.
To me it's not worth learning the "in-between" tools for the extremely limited circumstances that I'll need something less than IntelliJ but more than vim/bash. I even hesitate to customize my vim much because I need to rely on it as a fallback on nearly any system, and on novel systems I can't rely on my customization.
I'm not dogmatic about only sticking to these - I'm comfortable with VS Code when it's what my employer/workgroup provides, and I'm comfortable with Sublime, as it's particularly portable (can be run off USB). So sometimes Sublime is the fanciest option.
But anything in between JetBrains and vim needs a real "reason" to bother investing the time to learn.
And that's not the "toolbox" license suite - I do all that for the price of a single Ultimate subscription.
[1] https://www.jetbrains.com/all/
Great thing about JetBrains is they work out of the box unlike vim where you need to spend a whole month customizing just to get back on working on the project with 20 random plugins out there and it's still probably only 30% as good as JetBrain's.
VSCode is being developed rapidly and I see many good plugins but it's still quite far behind except for the launch speed. At least it could be seen as a competitor to make sure JetBrains will keep innovating and keep the performance of their IDE sane not to lose customers to VSCode.