Hello there!
Welcome to the third edition of Pragmatic Shorts!
As always let me invite you to read David Thoma’s and Andrew Hunt’s The Pragmatic Programmer. This book is an awesome compilation of years of knowledge and experience from the hands of experts in software engineering, and by no means this posts are intended to skip the book, if you have the chance to read it you’ll have a good time in learning from them as much as I and many other people has.
Pragmatic Shorts, are cheat-sheet like space with a point like summary of the parts of this book that I’ve considered most relevant in my day to day job as a Software Development Engineer.
For more context check the introduction to short #1, A pragmatic philosophy, also you may want to check other posts on the topic:
For this post I decided to change the dynamics a little bit and instead of jumping directly into the relevant points summarizing the authors work, I’ll add a bit more of my personal opinion and experience with some examples on the topics covered.
The basic tools
As a software developer, manager, PM, TPM, process engineer, or which ever position you could think of related to software delivering, the rule is that the project gets delayed. There’s always an emerging bug, a sudden requirement, something to jiggle with the tickets or Kanban cards to see which gets budget, which is taken, which one is sent to the backlog or to the nice to have bucket. Work to do is never missing, but rather short on people to do it.
You need tools to allow to you improve your day to day workflow in order to deliver value to your company or personal projects, which at the end puts food in your table and makes your resume.
You won’t rip off the grass with your hands if you have a machete, neither use the machete if you have a lawn mower. Same applies to your work. You won’t code a critical piece of software in the classical notepad if you have an IDE.
Many new programmers make the mistake of relying only in one tool which I’ll advice you to explore and try many tools to then select the ones that easy your day to day workflow.
Plain text
As an SDE at Amazon, one of the key topics you cover is writing documents. Design documents, a period’s planning documents, ideas document, error correction documents, etc.
It’s easy to think, why will you write so many documents instead of focusing on writing good code and make good designs? There are many answers to this questions, but I’ll start with this: The palest ink is still better than the best memory.
Our base material is knowledge and requirements area type of knowledge that we express in our designs, implementations, tests and documents, and in most binary formats the issue is that the context to understand the data is separated from data itself, plain text can achieve self-describing data stream that’s independent from the application you created.
You won’t share the link to Git to your PM and ask him if that’s what he needs. Neither will onboard a new developer and tell him to just read the code. If a developer has the knowledge of the whole big picture she’ll be able to communicate the intention, but if she leaves the company you’ll add an additional working load.
Plain text written document like a wiki, or a google doc, or even a Readme.md
will offer a source of both documentation and intention:
- Keep knowledge in plain text – It’s an insurance against obsolescence as a human readable forms of data and self-describing data which will outlive any other complex form
- Has leverage: Virtually any tool can operate on plain text (CLI, IDEs, etc).
- Allows easier testing: Plain text creates synthetic data to drive system tests which makes it simpler to update or modify test data without having to create any special tools to do so. Also a plain-text output from regression tests can be easily analyzed with a simple script.
- Lowest common denominator: Plain text helps ensure that all parties can communicate using a common standard (SDEs, SDMs, PEs, PMs, TPMS, VPs, etc)
A part of this chapter that something that caught my eye is the Unix Philosophy: do one single, small task very well using a common underlying format. A line oriented plain-text file
Shell games
There’s for sure an spectrum of developers in terms of shell using. In my personal experience I’ve found two interesting groups, one in each extreme of the spectrum. Those who hate using a shell and those to love it. Curiously enough, I’ve found most of the people in the first group favor windows and try to work in it, while the second group is more inclined die-hard linux fans.
I personally love the CLI but will use the OS that best fits both the technology to be used and the environment in which I’m fastest to get things done.
Paraphrasing the authors analogy, just as every woodworker needs a good workbench, every programmer needs a good command shell.
Here are some topics to consider:
- A GUI will only allow you to as much as the intention of the GUI’s designer. Here WYSIWYG (what you see is what you get*)*
- Working only on GUIs misses the full capabilities of your environment, you won’t be able to or have troubles to:
- Automate common tasks nor
- Use the full power of the tools available to you
- Combine your tools on a customized macro tool
- User the power of command shells – Shells allow for a lot of flexibility, automation, quick workflows, and you may customize your shell to look as you wish it to look like with your own personal shortcuts and quick command completions (as for me that I’m a lazy writer)
Power editing
- What is your profession? - I edit files for a living
As a developer your work involves many things, but at the end you create and editing plain files. This is the place is the entry point from your intention to the computer’s execution.
Before covering the points of this section the first thought that comes to my mind are 2 different subjects: the person that is starting into the programming world and is troubled to find which is the best code editor to specialize at and the person that is married to Atom, VIM, VSCode, IntelliJ, Emacs, etc.
This is not the topic for a war, neither has to be for fanaticism. Some editors have features that may or may not make them better then others for certain needs, but at the end the best one is the one that works for you supports your workflow. Is always good to explore but editors are like a phone, you stick to the one the serves you the best.
If you say Vim is the best, but need to use the down arrow to get to line 254 (yeah, down arrow, not even j
key), you seriously need to either learn how to use it right or better switch to something easier like VSCode.
In the same fashion if you are paying IntelliJ Ultimate and use only Ctrl-F to search the usage of a method, nor any of the pro features, then it’s cheaper to better use something free if you are using the basics.
Consider the these:
- Achieve editor fluency – You need to be able to manipulate text as effortless as possible, text is the your raw material. Use as many editors as you need or want, but work towards fluency in each.
- Learn the commands that make your life easier
- Every time you find yourself doing something repetitive get into the habit of “There must be a better way to do this” find it and implemented.
- Grow your editor: The most powerful code editors are built around a basic core that is augmented through extensions, when you bump into a limitation search for an extension
- Dig into your editor’s extension language and work it out to automate repetitive tasks you have
In my personal day to day, normally I use VIM for quick scripts, shell files, YAMLs, config files and editing anything in my EC2, IntelliJ for big Java packages, VS code for working on metadata and for quick git managing, and sublime to mock ups, JSON inpus/outputs and troubleshooting payloads.
My VSCode is filled with lots of extensions, my IntelliJ supports all my dependency manager and allows me to quickly work across many micro-service packages from the same product, my VIM is totally customized and looks amazing which is a treat to the eye, and my sublime is only divided by columns with only my mocks of the tasks at hand.
Version control
A version control system (VCS) is by far an essential tool in now days development. In the early 90s the crew from idSoftware used no VCS to develop Wolfestein3D which is pretty amazing! However they also turned a machine designed to see and edit text files into a gaming station with the computational power of a 1977 machine courtesy of intel’s 386DX + MsDos, they are outliers in this topic. Learn more in Game Engine Black Book Wolfenstein 3D from Fabien Sanglard.
You can’t edit you files and send it by email to your teammates, no matter how organized. Neither use a shared directory and expect to have a smooth flow of development, you’ll quick fall to a lack of control pit.
I know about people who made it possible, however there is a really high productivity and troubleshooting cost associated to it.
It doesn’t matter if your are just in college, use a VCS as early as possible in your career, motivate your classmates to do so. The first time there’s a bit of frustration from the lack of expertise and because you simple won’t know how to use git yet. It’s all normal, we’ve all been there and sooner or later you’ll too.
- Always use a version control system – A good VCS will let you track changes and know who made what, what changes between today and last week, how many lines changes and which files are edited the most
- Branching out: Isolate islands of development into branches (but beware of not overdoing it, it is hard to merge way too many branches)
- Have a look into a central repository and take advantage of a ton of integrations to make the flow easier
Debugging
It isn’t uncommon to have those days in which you spend more time debugging than writing actual code. We’ve all been in that point where you missed a bracket and the code breaks at a random place or misspelled something.
You could have many paths to troubleshoot, from those console.log("here!")
to elaborated debuggers. However, since debugging may take a great amount of time, be wise and allow yourself to use tools that will make your workflow smoother.
Let me tell you about Eric, a Process Engineer who’s also an SDE whom I work with. We work on a complex product that involves many parts. We our teams own one of those parts.
When our product fails, we need to dive deep into modular elements of a big chain, which involves a lot of steps to reproduce, including a lot of searching in the developer tools to extract data. Is a tedious work. It turns out that Eric is able to use 2 or 3 sets of hot keys to extract the data we need by creating tamper monkeys, bringing down precious minutes of reproducing time.
Any tool that supports reducing this time is your ally.
The first important factor in this process is likely to be the psychology of debugging
- Fix the problem, not the blame – It doesn’t matter who’s fault for a bug. Your problem will be to fix it, not pointing the fingers
- Don’t panic – (First rule of debugging) Adopt the right mindset and turn off your defenses, let go of your ego, tune out any project pressures and get yourself comfortable.
- Try to discover the root cause not it’s particular appearance. The devil hides in the details.
Once your mindset is working in your favor move the technical support:
- Make sure that you are working on code that’s built cleanly with no warnings, use the higher possible level of compiler warnings.
- First gather all relevant data. It’s easy to be mislead by coincidences and you can’t afford to waste time debugging coincidences, be accurate in your observations.
- Is possible watch the user who made the report of the bug in action to have a sufficient level of detail
- Interview the user to gather more data than initially given, your point of view may be very different the the user’s
- Brutally test both boundary conditions and realistic end-user usage patterns
Having a debugging strategy will support your work as well
- Reproduce bugs, the best way to fix a bug is to make it reproducible
- Go even deeper, and reduce the work you need to reproduce it, try to do it single command specially when it takes a long series of steps.
- Failing test before fixing code – Force yourself to isolate the circumstances that display the bug. Writing the test informs the solution.
Everything has been very smooth until now, but you can also face the case when the bug lies some where among a block of 50K lines of code. Or what about if your don’t get a crashing code but the result is simply wrong. How to tackle this?
There’s not a universal recipe, but consider these advice:
- Read the damn error message – First look into the problem, don’t just jump into the code where your suppose it is, read the error stacktrace
- Look at the place it crashes or fails the output and work backwards
- Feed the algorithm with the same failing data and make it crash in the same way
- Use a debugger like Javascript’s
debugger
. A tool to see the data in real time instead of just watching the final output - Use binary chop: as equivalent of binary search, chop your code or stack trace into halves. Comment or test half of the code, search in half of the stack trace to isolate to which half the error belongs to and then repeat it again. Also you can make half-way releases which will be intermediates between the last working code and your current changes until you find the chunk where the crash happens again.
Alongside the latter, another powerful tool is logging. Almost at a weekly cadence I face an issue that would take way too long and hard work to troubleshoot without logs
Debuggers focus on the state now, sometimes you need to have a log of what happened over time, the stack trace only tells you how you got there directly, but sometimes it’s complicated to see what happened before.
Tracing statements are little diagnostic messages, they are particularly effective at diagnosing several classes of errors that debuggers can’t, they are invaluable when time is a factor: concurrent, real time and event based applications.
Another funny advice is Rubber ducking, you may read the story of this name here. Often when I hear or say the phrase “Let me pick your brain” means I’m into rubber ducking someone else, which happens every once in a while friend Eric.
- To rubber duck, explain your issue to someone else. Explicitly state things that you may take for granted when going through the code yourself, you’ll probably find yourself the answers of what you are missing by taking it to someone else.
Another important factor is process elimination:
- It’s much more likely that a bug exists in the application you are developing and way useful to first assume that the application code is incorrect rather than to assume that the library is broken.
- Even if there’s a problem in a third-party code, eliminate you code before submitting a bug report
- In the author’s words: “Select” isn’t broken. This relies on a story of a developer that insisted on a bug in a third party library which was fixed by actually reading the documentation
Following this, consider the element of surprise, is you’re surprised by a bug, maybe you didn’t test all boundary conditions
- Don’t assume it, prove it – Accept that one ore more assumptions is wrong, don’t thrust that “you know a piece of code works”, prove it first, prove it in the same context, same data, and same boundary conditions
- Then, beyond merely fixing the bud, get to the root cause and determine why it wasn’t caught earlier. Are there other places in the code susceptible to this same bug? If it took too long, ask your self why.
- Think about how you can make it easier to find this bug next time, build better testing hooks or write a log file analyzer
- When a bug is the result of someone’s miss-assumptions discuss the problem with the whole team, others can make the same wrong assumption
To finish this section, I’ll share the book’s literal Debugging Checklist
- Is the problem being reported a direct result of the underlying bug or a symptom?
- Is the bug really in your framework? Is it the OS? or is it the code?
- If you explained this problem in detail to a coworker, what would you say?
- If the suspect code passes its Unit tests, are they complete enough? What happens if you run the tests with this data?
- Do the conditions that caused the bug exist anywhere else in the system? Are there other bugs still in the larval stage, just waiting to the hatch?
Engineering Daybooks
Finally, to conclude this tools section, you may keep an engineering daybook, a sort of journal to record what you (or the team) did, learning, idea sketches, readings from monitors or anything to do with the work. You may label them with a time range in the cover of spine of the book to support searching.
Benefits:
- More reliable than memory
- Place to store ideas that are not immediately relevant
- Work as a rubber ducking session