Clean Code - Part 2
Clean Code's advice on how to write readable functions. Plus, an introduction to WebAssembly and how to build a fast, concurrent database in Rust.
Today we’ll be talking about
Clean Code’s recommendations on how to write readable functions.
layers of abstraction in your code
descriptive function names
number of arguments
avoiding side effects in your functions
An Introduction to WebAssembly
A crash course in JIT compilers
A crash course in assembly
Working with wasm modules
What makes wasm fast
Building a fast and concurrent database in Rust
Quastor Daily is a free Software Engineering newsletter sends out FAANG Interview questions (with detailed solutions), Technical Deep Dives and summaries of Engineering Blog Posts.
Clean Code - writing readable Functions
Here’s a summary of Clean Code’s advice on writing readable functions.
This advice is geared towards functions written in an OOP language, although many of the concepts carry over to other language paradigms.
Principle 1 - Small!
The majority of your functions should be less than 15 lines long, and they should hardly ever be more than 20 lines long.
You should always be able to fit the entire function on your monitor, if you can’t then you probably need to break the function up into separate functions.
If you’re following this principle, it means that your function will not be large enough to hold several nested structures. You won’t be able to fit a loop, and then a nested if/else statement inside that loop, etc.
Instead, you should make those nested structures separate function calls. This also adds documentary value since those functions will have nice descriptive names that explain what they do.
Principle 2 - Do One Thing
Another way of stating Principle 1 is that your function should only do one thing.
A good heuristic to use is to see if you can extract some code from your function and turn that code into a new function with a name that is not just a restatement of its implementation.
If you can do that, then that suggests that your function is doing multiple things and you should break it down further.
Principle 3 - One Level of Abstraction Per Function
Your programs can frequently be divided into layers of abstraction, where each layer represents a different model of the same information and processes, but in a different manner.
An example is if you’re developing a program to scrape a website, parse some data and then put that data in a database.
One layer of abstraction is the HTML string you get after sending an HTTP request to the website’s server.
You take that HTML string and send it to your second layer of abstraction, where an HTML parser converts that string into an object where the HTML page is represented as a nested data structure (read up on BeautifulSoup if you’d like a more concrete example). This is the second layer of abstraction.
The third layer of abstraction could be where you extract the specific data you need from the HTML page object and store that data in an array.
The fourth layer of abstraction would be where you write the data in that array to a database.
The statements in a function should all be at the same level of abstraction.
You should not be manipulating the HTML string object and writing to the database in the same function.
Instead, Clean Code suggests The Stepdown Rule, where your code can be read as a top-down narrative.
Every function should be followed by a function at the next layer of abstraction. This way, as you read the program, you’ll be descending down one layer of abstraction at a time as you go through the list of functions.
Principle 4 - Use Descriptive Names
If you’re following the first three principles, then coming up with a name shouldn’t be too difficult.
The smaller and more focused your functions are, the easier it is to come up with a name.
Don’t be afraid to make your names long. A long, descriptive name is much better than a short, ambiguous name. Your editor’s autocomplete function should make it easy to write code with long function names.
Also, use a naming convention that allows multiple words to be easily read in function names.
Principle 5 - Prefer Fewer Function Arguments
The ideal number of arguments for a function is zero. Having more than three arguments for a function should be avoided.
Having lots of function arguments makes it harder to read and understand the function since it forces you to know extra details (and those details are usually from a different layer of abstraction).
Additionally, having lots of function arguments makes it more difficult for you to write tests. You have to write all the test cases to ensure that all the various combinations of arguments work properly!
Principle 6 - Avoid Side Effects
A Side Effect is where your function modifies some state variable outside of its local environment, in addition to returning a value(the intended effect) to the invoker.
An example might be a function that reads a certain value from a database and then returns that value.
If that same function also modifies a nonlocal object in some way (let’s say it appends the value to a static data member), then that modification is a side effect.
Clean Code recommends you avoid side effects.
Your function should either change the state of an object(s), or it should return some information. A single function should not do both!
Principle 7 - Use Exceptions, not Error Codes
Your functions should rarely return error codes. Instead, if something goes wrong, you should throw an exception with a well-written error message.
One issue with error codes is that it leads to deeply nested structures. When you return an error code, the caller must deal with the error code immediately.
On the other hand, a caller can separate the exception handler logic from the other code (a try, catch statement with separate the try logic and the catch logic).
Clean Code also recommends you extract the bodies of the try and catch code into functions on their own. This obviously helps you stay below the 10 line long function length and also allows you to document your code when you pick well written function names for the two functions.
WebAssembly has been taking the developer world by storm since it’s release in 2017.
Mozilla wrote an awesome series giving an introduction to WebAssembly. Here are the chapters…
A crash course in assembly - A brief intro to assembly and how compilers translate high level programming languages to assembly.
Noria is an open source storage backend written in Rust that’s designed for read-heavy applications.
Noria pre-computes and caches relational query results so that reads are extremely fast. It also automatically keeps the cache up-to-date as the underlying data changes.
This talk is from Jon Gjengset (a PhD student at MIT’s Parallel and Distributed Operating Systems Group) on Rust and Noria.
Overview of Rust
Basics of Rust concurrency
The architecture of Noria
If you’d like to learn more about Noria, check out the paper a group at MIT published on it’s implementation.
This email got a bit long, so we’ll skip the coding problem/solution for today to stay under 5 minutes.
Stay tuned for tomorrow’s interview question!