I’ve noticed that my productivity is directly correlated with the size of the feedback loop. Even little things like inline errors or a particular keybinding that you use can mean a big difference, I feel! Please feel free to share anything—I’d love to hear about your environments!
A few things that come to mind (from small scale to larger scale optimizations):
- Editor keybindings, editing code fast and conveniently (and having fun while doing it). Vim helps a lot here, but knowing the shortcuts in any editor (or other applications you use) is important.
- Inline linting and compilation errors in the editor.
- Static typing, your editor will already let you know if what you’re doing makes sense, and gives you suggestions on what you should do next.
- Automatically running automated tests inside the editor, or just in a separate terminal tab.
- Automatically recompiling code whenever it is changed. Something like Vite makes this very fast for web dev.
- Hot module reloading avoids reloading the page after every change.
- Automation in general avoids human errors and reduces the number of feedback loops. I quite like do-nothing scripting for this.
- Using Docker avoids parts of the feedback loop for setting up a project entirely. Set up scripts improve it further.
- Good documentation for setting up a project avoids the feedback loop of asking other devs for help.
- A fast and convenient development environment makes things nicer in general.
- If you use the conventional commits commit message format, then running the commit message linting right after writing your commit message.
- Running code checks any time code is pushed. Even if you don’t run linting or check for compilation errors locally, your pipeline should still catch any errors as early as possible.
- UI tests check if the main flows in your application are working, avoiding manual work of testing after deployments.
- Continuous integration, merging code to the main branch as often as possible reduces conflicts and makes working in parallel with others easier. You get feedback about merge conflicts much faster.
- Continuous deployment, you get quicker feedback from clients about your deployed code.
- Error monitoring, know about runtime errors before clients report them.
- Feature flags allow releasing features to some customers earlier, getting feedback earlier. This also enables decoupling releases from deployments.
- Analytics helps to understand user behavior, allowing you to quicker make decisions about how to develop your product. This allows avoiding user interviews in some cases.
- Sitting in the same room as your team (devs, designers, pms) shortens the feedback loop of asking them for input while working on your tasks. Remote work makes this more challenging, of course.
- Having detailed designs in Figma shortens the feedback loop of asking designers for input. E.g., how many pixels should this padding be?
- Code reviews allows you to get feedback on your code before it’s merged and causes problems in the future.
- Design reviews (showing your implemented designs to your designer before merging) allows you to get feedback on your implementation earlier and avoids a new pull request with fixes.
- Sitting down with your QA and testing your code together allows you to find mistakes in your code earlier than when QA tests your code in production after merging.
- Releasing features often in small increments rather than a big release after many months of work (starting with the MVP and building on top of that) allows you to get feedback from clients often during the process. It’s also easier to pivot after a month of work rather than after 6 months. Splitting up features into smaller self-contained chunks is an art form on its own.
This ended up being a pretty long list, but I hope it was useful!
This is a hero list! Props
(just kicking things off here)
learning Vim was really great for me; I don’t think I’ll ever learn everything it has to offer but picking up a new shortcut is always so much fun, especially if the code isn’t the most exciting.
Keybindings really can make a huge difference, whether they’re existing ones you discover or ones that you create.
I remember discovering VS Code’s default key binding for “going back” to the previous cursor position (going across files), which is Ctrl±. That really reduced the mental load of keeping track of which functions/files I was visiting and made me feel more at ease jumping around, knowing I could easily get back to where I was. If you need more context on some code you’re writing/reading, you can more easily go off on little tangents to gather whatever context you need, and that speeds the whole process up.
Feedback loops are super important! For momentum, for reducing burnout, for implementing/debugging, everything. I think of it mostly as a tooling problem - the point of maintaining and improving your tools is to maintain/improve your feedback loops.
For me it’s about this question: How quickly and easily can you verify that the code is doing what you think it’s doing?
This is what I love about writing Clojure - you can write and evaluate abritrary bits of code (functions, expressions) without leaving the editor. Invoke a keybinding to send the current expression to the running repl, and the resulting value is returned, and can be interactively explored/walked in the editor. It makes for a fun interactive dev-loop, and is a nice way to design a solution to some problem. (A caveat is that getting into a working repl is non-trivial, as it’s dependent on your editor+plugins. It takes a bit of learning and unfortunately isn’t beginner-friendly.)
Vim and emacs are also excellent for improving you feedback loops - both take some investment and some discomfort in the beginning, but ultimately you get out what you put in, and soon you can imagine and realize better workflows without much effort (adding your own functions, keybindings, hydras, etc). VSCode and other editors are also hackable, to some extent.
Mostly I think it’s important to hack on your tooling on a regular basis, at least once a week or so.
My old boss used to say he expected us to keep ‘sharp knives’ (as in cooking). I think companies should make time for the devs to work on tooling to improve these feedback loops - it’s the hiccups in the workflow that build up and lead to burnout/fatigue. Smooth workflows can actually be energizing instead of energy-draining!
My whole work environment is tightly integrated ensuring I can use the same tools nearly everywhere. Things like keybindings (deleting a sentence, spellchecking a region, multiple cursors), macro’s (ad-hoc repetitive command sequences), the consistent mostly text-based visual look & feel. All of this lowers the cognitive load.
Comparing to an IDE, Emacs is more of a hyper-configurable integrated work environment. In my case, my code editor (Emacs), my knowledge base (org-roam), my tasks manager (ad-hoc on top of org-mode), my email client (mu4e), my tiling window manager (exwm), interaction with git (magit) and git issues and PRs (forge) as well as some other tools are controlled from Emacs. I call them ‘my’ because they’re sometimes slightly modified to scratch my own itches. I could integrate my calendar but Google’s webdav APIs seemed flaky at the time and FireFox only gets some consistent keybindings.
Just a few more years and Emacs will turn 50 years old. You never know what the future will bring but there’s a reasonable chance I will not have to throw away what I have learned so far.
Some examples of this integration:
- When I start developing on a project as full-stack I usually have a
M-x develop-projectname
command that boots up the application, arranges my windows with the right folders open, backend and frontends started, and a place for FireFox (not integrated, only uses some of the same keybindings) - It is not uncommon for me to receive about 100 emails in a day, some just informative and some requiring action. Processing these can lead to tasks or just information. In any case, treating them and doing actual work on the same day requires focus and a smooth path from throwing it away to drafting out tasks.
- An email can lead to an action to be taken on a server. When managing a server (local or remote), I’ll outline the tasks to execute. I can execute these tasks through org-mode’s code-blocks on the remote host and have a read-back of commands and output. In this use my knowledge base becomes similar to a Jupyter Notebook but integrated with all the rest. I can also reuse the results whilst working on it and I can mail the read-back to whomever would need to have the result in a readable email.
If you want to come to the dark side and like VIm’s keybindings, you may want to use Emacs’s evil-mode and keep them. It might just be the best of both worlds.
I love the idea of Emacs (especially of Doom!) I’ve played around with Doom a bit a few months ago and it was enjoyable to use, if a bit difficult to learn. Unfortunately, getting native compilation to work on Mac OS was making my install really brittle, but I might give it a second try after your comment.
- When I start developing on a project as full-stack I usually have a
One thing I’ve done is to think a bit more about how to verify the behaviour of individual components without running the entire thing end-to-end. From there, there is a wealth of tooling to run things automatically for me - unit tests, shell scripts, CI/CD pipelines - so I get feedback as quickly as I can think (which isn’t so fast actually!).
(Almost to embarrassed to ask a question s as simple as this, but here we go…)
I hear a lot about shell scripts and how useful they are, but my expertise in Bash is pretty much limited—know I can pipe cat into vim, basic things like that—could you share some examples of what you can automate with shell scripts that you personally use often?
I used to love IDEs with immediate feedback on the code, and a modern VSCode setup can really shine in this regards. But these days I’m going for a minimalistic approach where I don’t want to see anything in my screen but the code. I use Neovim and while I use plugins for formatting code on save, my screen is absolutely code only. No linting hints, no function definition appearing when I hover thr mouse, nothing at all. It’s far less distracting and you also feel much less constrained, even if I unconsciously already write the code in a way that the linter doesn’t complain (too much) later. I haven’t noticed any drop in my productivity in the last year I’ve been doing this.
@Blackthorn Same, that’s why I recommend using the ‘zen’ mode JetBrains IDEs where necessary (ctrl|cmd+alt+z) -> https://www.jetbrains.com/help/idea/ide-viewing-modes.html