Last Updated: 2024-11-23
I was thinking about what specifically makes someone professional in their coding abilities and came up with a list
for the benefit of myself and the people I work with - a set of standards to
work towards.
Consistency
Doing things in a standardized and consistent manner
What this means in practice
- using
editorconfig
(and associated editor plugins) to ensure that
white-space (etc.) works across different operating systems (e.g. without
this, hitting save in a windows-produced file in a unix system would cause
every line end to change and create a mess in git)
- using linters and code-formatters (and associated editor plugins) to
standardize coding style across the team
- using standard emails/usernames and passwords in our test data
- re-using code from project to project as much as possible, improving these
components as life goes on.
- being conscious of naming conventions in each platform and sticking to them.
E.g. python functions use
snake_case
whereas JavaScript uses camelCase
.
Django model names are singular (e.g. Vote
not Votes
). If in doubt, Google
or ask.
- Call variables the same thing across the project. Agree with your team the
names of entities (e.g. "events" vs. "experiences") and make sure the
front-end and back-end code is updated.
- writing git messages in a standard format
"{PRESENTTENSEVERBWITHCAPITAL_LETTER} ..." - e.g.
"Fix bug with loading packages" or "Constrain data to avoid duplication"
- sticking to our standard set of commands for interfacing with a program. At
the moment, this is things like
make run
and make build
etc.
Parsimony
Having nothing more than necessary to reduce cognitive load.
What this means in practice
- use a
.gitignore
file and ensure that nothing that gets committed that is
not absolutely necessary. Otherwise someone reading the codebase has to figure
out what the hell those files are for and it costs them time. If there are 5
of them and 1 of you, this cost multiplies.
- unused code must be deleted (not commented out)
- log statement for short-lived debugging purposes should be removed before
committing (vs. log statements for general informative purposes - which are
fine to keep in the code)
- code comments that only describe what the code does are not useful. Code
comments that describe why you did something in a strange way (e.g. due to
constraint xyz) are very helpful and encouraged.
- delete any un-used database columns as soon as you know they are un-used
- learn the distinction between
public
and private
methods and aim to
have as few public
methods as possible. Roughly speaking, private methods
are implementation details whereas public ones are those expected to be called
externally. Someone else using your code should usually only need to
understand the public methods. Note that private/public may be communicated
differently in other platforms (e.g. in JavaScript export
ed functionality is
similar to public, whereas non-exported is private)
- mark unused variables with the underscore prefix (e.g.
_name
) to communicate
its
- limit the possible values in database fields. E.g. if you have a boolean
field, by default it can contain 3 values -
true
, false
, and null
. But
most codebases only need true/false. By adding a NOT NULL
to the column, you
limit the possibilities to true/false
and make everything simpler.
- when the situation calls for it, use language primitive (e.g. arrays or
objects or custom data classes) into of reaching for whatever the standard
thing your programming environment offers you (e.g. making everything a
database record)
Conscientious
Essentially double-checking your work and actively searching for errors.
What this means in practice:
- never commit a line of code without having verified it works (either with a
software test or a manual test)
- be aware of sensitive data (e.g. secrets or database fields that need to be
encrypted) and treating them with appropriate care (e.g. put in an ENV file)
- anticipating edge cases (e.g. what happens to my code
event.venue.start_time
if venue
is null - do I want an error page?)
- use a visual git tool (like Git Gui or the GitHub app) to manually proof-read
every line of code and comment before committing it
- spelling. Function names and class names must be spelled correctly, otherwise
it becomes difficult other members of them team calling those functions with
the standard spellings will get weird errors. Documentation, code comments,
and git messages should also be proof-read with a spell-checker. If you are a
non-native speaker, write the comment in something like Grammarly and then add
it to the code.
- you should never deploy without ensuring the tests are passing (where
available)
- ask yourself these 3 questions:
- Am I missing anything?
- Can could go wrong?
3.
- you should work towards being able to write software tests in your platform so
as to automatically verify correctness.
- if you make a deploy to production, you need to babysit it afterwards. This
means looking at the output of the deploy script, visiting the website and
ensuring it broadly works, keeping an eye on the logs for a minute, and
keeping an eye on our error tracking code.
Communication
Keeping everyone in the loop and proactively helping team-mates avoid WTF
situations and confusion.
What this means in practice:
- If you discover technical surprises (e.g. "this data is way different than we
expected), communicate it immediately.
- If you added an environmental key that other team members need, be sure to
share it with them right away (otherwise they'll be unable to run the code
when they pull).
- If you are making a lot of changes to the database or installed packages and
someone else is working on that same codebase, communicate that they'll need
to migrate.
- If you change the backend API, then tell the front-end team their code will
need to change.
- If you have to change someone's code for a surprising reason, explain why it
was needed in the git commit message.
- If you change the functionality of something (e.g. a React component called
SelectBox
to do something else as well - e.g. show check boxes) then rename
the item to match its new functionality (e.g. SelectBoxAndCheckBoxes
)
Note: This is bit of a bad example since it would probably be wiser to split
these two into separate components.
- If something tripped you up and you think others can learn from it, make a
Today I Learned (TIL) post in the
#development
channel.
- If you think you're going to work on the same files as another team member,
coordinate with them so as to avoid git conflicts. E.g. ask them to commit
their changes and give you two hours to work on the file before sending it
back to them.
- Explain to people the why behind any given choice. The idea here is
that people understand things better when they know the context.
- Your git commit messages should give your team enough info to know what
changed. Prefer committing often (e.g. 4-12 times a day) with each cohesive
piece of work (e.g.
Add translated strings
, Add new columns to experiences
table
). A secondary advantage of this way of working is that it encourages
you to do things in smaller units, which is a cornerstone of good engineering
(see below). Pragmatically speaking, use git add -p
to add your changes
piecemeal (or better yet, a Git Gui tool).
- In written text, it's very easy to accidentally read aggression into text that
was meant kindly. Knowing this leads to two consequences:
- Put emojis in your text and make it extra warm in tone
- Assume any messages directed at you were meant kindly (instead of meant
critically)
- Keep in the mind the need for your software to communicate with the end-user.
Most codebase need a general info/error message display box to do things like
reassure the user their data was saved, or alert them that their internet
connection was down.
- Structure your thoughts before communicating them. For example, instead of
sending 7 one-sentence slack messages, combine your ideas into a structured
paragraph. This helps you to think more clearly and it also helps the other
party to understand the situation better.
- Have as many conversations as possible in public slack channels instead of
private ones. You probably underestimate the value of this info to other
team-mates.
- Have humility. Even if you feel dead sure a bug was caused by someone else,
you might be wrong and it could be your fault. Therefore don't point fingers.
Just observer what's happening in the code and offer to look into things
together. It would be super embarrassing for you (and damaging to the
relationship) if you assumed it was someone else's fault but it wasn't.
- Be mindful of the trade-offs involved in asking questions to other team
members. Specifically, if you're unsure about something (e.g. why a package
was missing), spend 5 minutes trying to fix it on your own first. This is
because learning to solve problems on your own helps you grown and it avoids
interruption costs on other team members. If, however, something has you stuck
for 10-15 minutes, definitely reach out.
Growth and Mentoring
Cultivate an attitude of wanting to improve.
What this means in practice:
- Pick a goal for the quarter (e.g. "get good at React", "master flexbox") and
spend 15 minutes a day working towards this (e.g. at the end of the workday or
in your own time). This should be fun for you, so try finding a YouTube video, book, or
course you like, or pick a side project. We are happy to pay for these materials. For the really ambitious,
choose goals in the programmer competency matrix
- It's part of everyone's job to educate people on their team. If you fix a bug
then couldn't, it's often worth giving them a bit of advice about how to
approach things. A good approach is to first ask the junior programmer what
their opinion is (to see how they think) then give your own perspective
afterwards.
- If something has kept you stuck for more than 15 minutes, ask someone else for
assistance. The more senior programmers are here in a mentoring capacity for you
and it's their job to offer 2nd opinions and help you get unstuck. This is a
win/win: it helps you grow as a professional and advance your career - and
it's also better for the company (and your performance) if you can solve a
problem in 30 minutes using a mentor's help vs. in 3 hours without their help.
- Programming is largely about not being wrong. Keep a diary of problems that
took you more than an hour to solve or of mistakes you made. After a year of
doing so, you'll be a way better programmer.
- Freelancers may ask for tutorial sessions to be taught a new technology by
programmers with more experience (but you cannot bill for these)
- Freelancers are expected to know the technologies they bill for. If they wish
to write code in a language they don't know yet, that's something we can
accommodate, but because your speed will be less, we should discuss
temporarily reducing rates for that work until you're up to speed.
Introspective tools
Have at your disposal excellent information and tools about how your code and
how your program is running, in order to debug it quickly.
What this means in practice:
- You should install any available developer tools for your ecosystem (e.g. the
React Developer Tools plugin for Chrome) and learn how to use it.
- You should know how to use a debugger in your language
- You should use Language Server Protocol plugins for automatic refactoring and
best-in-class autocomplete.
- You should have exception reports in production
- You should be able to access the logs in production with a single command
- You should be able to access an interactive console (or
repl
) for your
programming language environment and be able to carry out micro-experiments.
- You should be able to jump to function definitions in the code from 3rd party
libraries to inspect their definitions, add log statements, or insert a
debugger.
- You should be able to access the database directly both locally and in
production.
- Your production logs should contain post parameters (where non-sensitive),
user ids (where appropriate) and device-IDs in order to assist with debugger
- You should use background queue jobs for items that might fail transiently so
that they can be tried again.
- You should have continuous integration tests to discover test failures earlier.
Teamwork
Activities that enable the team to work more effectively.
What does this mean in practice:
- Figure out any possible blockers (e.g. API keys needed from client) and communicate this
as soon as you know.
- Pull the primary development branch a couple of times a day and integrate into
your branch. This helps us avoid git conflicts.
- Push every commit to GitHub as soon as possible (and commit often)
- Take responsibility for getting better at your time estimates. If team A and team B
depend on Team C's work, then an incorrect estimate can cause multiplicative
delays. We all know software estimation is hard, but with practice you'll get
better. Therefore please keep track of your where you over or under-estimate and adjust in future.
- When giving feedback (e.g. in code reviews), never make it personal, give the
contributor the benefit of the doubt (since they may know more about the
complexity of the domain than you do), and if you can't offer any ideas for
constructive changes then keep your mouth shut. Lastly, point out at least one
thing that was done well.
- Don't send broken code to team-mates - e.g. code with missing packages. This
causes everyone on the team to waste time. Ensure your code has everything
your team needs to build the software (e.g. that all dependencies are listed
that the README is comprehensive and accurate)
- Prioritize work that unblocks other team-members (e.g. if they need component
X in order to do their work, switch over to that)
- If something was difficult to understand (e.g. the logic of a mass
spectrometer domain) and you had to spend a few days understanding it, then
it's likely the rest of the team will also need that information. Therefore
jot down your knowledge in Slack so other people can pick up where you left
off.
- Make the complicated procedures (like setting up the project or deploying)
simple by creating commands that automate all this work.
- Write code using virtual environments or containers. It can be a nightmare to
get projects running on other people's machines (with totally different PATH
settings and different programming language and sql versions). Therefore, as
much as possible, create all-in-one systems that work on every one's machine.
- When working with a client, get the email address/phone number of a touch
point there and don't be shy about asking them all the questions you have.
They want you to understand their project and win.
- If your manager suggests a way of doing things that's wrong or problematic,
it's fine to go against them. You are closer to the problem.
- If the client wants something that's impossible or very difficult, communicate
this and we may be able to renegotiate.
- Be punctual for all-hands meetings (e.g. stand ups). Five minutes of your
lateness amounts to 25 minutes of company time. (For one-on-one meetings, this
is less important)
- Keep status tracking documents up to date (e.g. mark your issues as done as
soon as they are)
- If you're primarily a coder, push back against clients and managers that cut
your day into small blocks of time. Instead push back and try to schedule meetings
before/after you're in the zone (e.g. 1st thing in the morning, just before
lunch, just after lunch). It's important that you get solid blocks of time to
work.
Architecture
- Your primary job as an engineer is to use your intelligence. This is not the
same as coding. Rather it's sketching out (in your mind, on paper, or in your
editor) a couple of possible solutions and thinking through the consequences
of each in relation to the whole project. No-one becomes a 10x programmer by
typing code quickly; they get there by coming up with the solution that solves
90% of the headaches - there typing speed is irrelevant. A good rule of thumb
is to think of at least two possible alternative designs before writing any
code. For harder problems, I can recommend going for a walk and mulling over
it, or keeping it in the back of your mind over a weekend - maybe you'll get a
EUREKA insight when you're brushing your teeth.
- Often it can be a good idea to bring in a mentor programmer to talk through
your draft design before implementing it. Perhaps their experience can add something to the picture.
- In terms of engineering priorities:
- Correct
- Understandable
- Testable (similar to atomizable)
- Observable (possible to get an insight into what's going on)
- Elegant (dry)
- Fast
- DRY (Don't Repeat Yourself) is applied too quickly in many cases and attempts
to DRY up code can create complicated, bug-prone abstractions that don't
justify the savings in a duplication. As a general rule, if functionality is repeated once,
do not DRY up the code. Instead comment that it's used elsewhere. But if it's used
3 or more times, consider DRY-ing it then. (However with data - e.g.
taxRate
it's important that this is only in one place)
- As a consultancy, we become more efficient - and have less bugs - when we
re-used battle-tested code from previous projects. Therefore, as far as
possible, take existing code into your current projects.
Atomize
Good engineering consists of breaking your work down into small, cohesive,
"units" that can be re-used across the project, re-used in other projects,
easily tested in a REPL or in unit tests, and happen in enough isolation to
avoid merge conflicts.
What does this mean in practice:
- Use a workflow that breaks your feature into small bits of functionality that
built upon one another. E.g. if you need to scrape data from an API, start
with a method/class to get the right JSON using the API key (then commit). Next add
a method to convert that into domain objects in your codebase (and commit)
etc. Why? Because this encourages you to create smaller units, to commit more
often (avoiding conflicts) and think in terms of composition and layers. It
also helps beat procrastination since a big hairy task is much less scary when
broken into 8 smaller steps.
- You should do only one thing at a time in your git commits. Use
git add -p
to add your changes piecemeal (or better yet, a Git Gui tool)
- If you're writing something like a cron-command, don't put all your code
there without the command. Why? Because the cron-command cannot be tested
with unit tests or in a REPL. Instead use the cron-command to call a
class/function, something that can be used in isolation.
Other
- When you don't know how to do something (and neither does your mentor), ask a
question on Stack Overflow or reddit. You'll almost always get a great answer.
- Adjust the quality of your work to the project, to the humans risks of it
going wrong, the likelihood of that feature being used, etc.
- Be decisive and take calculated risks. Sometimes the only way to make progress
in a project is to make a decision. Sometimes these decisions will turn out
to be incorrect and have to changed. That's OK though, because of the
efficiency the teams gets from moving.
- If something is bothering you about the company processes or tools, bring it
up and we can discuss ways to make your life easier.