Maximizing the collaborative potential of your software


Pairing with the Future


Kelly Fox

Code is communication

Across time and space

How do developers spend their time?

(Based on how it feels.)

Ease of change directly correlates
with ease of understanding

Rebecca Parsons
CTO, ThoughtWorks

Code is written for humans first,
machines second

Projects are easy to start,
But harder to finish

Cache your context

Suggestions, not rules

  • What follows are starting points for discussion, and may not be appropriate for every situation.
  • Some suggestions may be easier to implement than others.
  • Use these guidelines to help you approach coding with a future-looking perspective. Imagine someone new to your codebase trying to make sense of the code you’re writing.

Project, file, and function structure

Approaches to Project Organization

  • Follow conventions for the language or framework
  • Arrange folders intuitively, from abstract to specific
  • Organize code by features or functionality
  • Keep related files and code “close together”

General file layout

  1. Imports — built-in, external, internal, each section alphabetized
  2. Variables common to the entire file
  3. Function and class definitions, before use
  4. Main program logic
  5. Exports

Structure of a function or method

  1. Variable declarations and assignments
  2. Early return (if necessary)
  3. Core function logic
  4. Return statement/value

Use blank lines to separate sections or logical chunks of code.

Sizing

Sizing Suggestions

Item Limit
Line Length 80-120 characters max
Function Arity 3 arguments max
Function Length 4-10 logical lines max
Class 2 pages max
File 3 pages max
Files in a folder Minimal scrolling

Good functions and methods


              int getRandomNumber()
              {
                return 4; // chosen by 6-sided die roll
                          // guaranteed to be random
              }
            

Tips for Writing Good Functions and Methods

  • Functions should focus on accomplishing one task.
  • Return early from functions when possible to avoid unnecessary processing.
  • Function layout should be: Variable declarations and assignments, then early returns, then core function logic, then the final return statement. Single blank lines should separate sections for clarity.

More Tips for Writing Good Functions and Methods

  • Code representing the primary behavior of a function shouldn't be buried inside an if/then statement.
  • Write short functions. This makes code easier to read, easier to reuse, easier to review, easier to adapt for other purposes, and easier to test.
  • When possible, avoid: sharing state with other functions, changing state, and side effects.

Arity: The number of function arguments

  • Try to keep the number of function arguments to three or less. It can sometimes be difficult to remember argument order with as few as two arguments!
  • If more arguments are needed, pass an “options” object as the last function argument. An options object as the last function argument allows you to change the number of arguments without refactoring all references to that function.

There are only two hard problems in Computer Science: cache invalidation, naming things, and off-by-one errors.

Naming

  • Pick obvious names that are representative of the value or behavior.
  • Choose names that help your code read like a narrative.
  • Use long names if they’re appropriate. Tab-completion makes typing them easy, and finding all uses of a unique name is a simple task.
  • Define all “magic” numbers or strings as variables to explicitly communicate what they represent. For example, NUMBER_OF_SECONDS_IN_A_DAY is easier to understand than 86400.
  • Check for namespace collisions. Will the name you choose cause confusion with another existing name?

Code Comments

When To Use Comments

  • API documentation, often for use with tools like Javadoc, JSDoc, Swagger, etc.
  • To specify or ignore linter rules, for tools like ESLint (JavaScript), flake8 (Python), etc.
  • Describing unusual code that you were forced to include for non-obvious reasons, e.g. to circumvent a known bug or deal with a poorly-written API. (Include a link to the issue, if possible.)
  • Illustrating program flow or data handling in code samples
  • Pseudocode, but only as temporary placeholders for actual code

When Not To Use Comments

  • Describing normal or conventional functionality. The code should describe itself.
  • Describing complex functionality. Refactor your code instead.
  • Preventing code from being executed, except for temporary debugging.
  • Keeping code around “just in case”. Delete it and rely on version control.
  • Managing “todos” or describing changes that need to happen later. Use an issue-tracking system.

Reasons to Refactor Code

  • DRY-ness
  • Simplicity
  • Clarity
  • Testability

Refactoring for DRY-ness

DRY = Don't Repeat Yourself

  • Extract repeated patterns from your code to helper functions.
  • Define common constants in a shared file.
  • Avoid over-generalizing functions, classes, or objects. Consider using function composition, inheriting from superclasses, or partitioning objects.

Refactoring for Simplicity

  • Avoid nesting. Try extracting nested code to external functions.
  • Avoid clever code. Cleverness often conceals functionality — save it for high-performance or coding challenges.

Refactoring for Clarity

  • Limit functions or methods to one purpose.
  • Move transformational code into separate functions with meaningful names.
  • Ensure magic numbers and strings are replaced by meaningful constants.
  • Don’t be afraid to create one-line functions!
  • Write more verbose code if it facilitates understanding and doesn’t adversely impact performance.

Refactoring for Testability

  • Only export what needs to be tested.
  • Don’t (deeply) test third-party code.
  • Get configuration out of your code. Use a file, database, environment variables, etc.
  • Use dependency injection to simplify testing of external services, varying environments, or complex objects.

Safety Nets

Testing

  • Good tests can explain how your code should work without looking at the code itself.
  • Only test what you need to test.
  • Bad tests can do more harm than good.

Automated Testing

  • Set up your development environment to “watch” files and automatically execute tests when you save changes.
  • Use a continuous integration testing service that executes your test suite after code is committed to source control.

Using Source Control

  • Work in a feature branch.
  • Commit early and often.
  • Write meaningful commit messages.
  • "Squash" your changes prior to merging.
  • Learn to use your tools before an emergency arises.

Using Third-Party APIs

Consider wrapping your APIs with generalized wrappers if you think a service you’re using may eventually need to be replaced. This lets you swap out the underlying service with minimal disruption to the rest of your code.

Documentation

  • Provide instructions for installing, further developing, testing, contributing to, and using your project.
  • If your code and tests are written with clarity, you may not need extensive documentation.
  • Leverage tools to document the code for you.

Continuous Improvement

Tools for Success

  • Use linters to identify problems as you type — eslint, pylint, etc.
  • Use “beautifiers” to format code according to agreed-upon conventions — JavaScript Standard Style, Black for Python, gofmt, etc.
  • Use EditorConfig and pre-commit hooks to enforce consistency.
  • Enable syntax highlighting.
  • Leverage type-checking if that’s an option.
  • Consider Dockerizing your entire development environment. (See also: VS Code's devcontainer.json.)
  • Use an IDE that facilitates all of the above with as little effort as possible.

Choosing new tools

  • Keep an open mind. Just because a tool or framework is popular does not necessarily mean it’s the best solution for your problem.
  • Find out what other people are using or creating, and why. Read books, blogs, and newsletters. Listen to podcasts. Watch videos. Attend meetups and conferences.
  • Don't "reinvent the wheel" unless you have a good reason to do so.

More Resources

Writing maintainable software
is an art that improves with practice

Questions

Thanks!

Kelly Fox