Hey Everyone,
Today we’ll be talking about
Clean Code’s recommendations on commenting your code
Try your hardest to avoid comments. Express yourself in your code.
Maintaining comments is extremely difficult
A couple good places to include comments
A Guide to Browser-Side Storage Options
Web Storage API
IndexedDB
Cookies
Cache API and more!
Plus some awesome tech snippets on
How the Have I Been Pwned website got pwned by Azure Cloud costs
TypeScript features to avoid
Go’s most important feature
Startups are shifting heavily to Remote hiring
Plus, an answer to our last (hard) interview question and a new question from Facebook.
Questions? Please contact me at arpan@quastor.org.
Quastor is a free Software Engineering newsletter that sends out summaries of technical blog posts, deep dives on interesting tech and FAANG interview questions and solutions.
Clean Code’s advice on Comments
Bob Martin’s Clean Code is a book that every programmer should read (and re-read).
We’ll be summarizing Chapter 4 of Clean Code, where Bob Martin gives advice on how you should think about commenting your code.
Summary
Comments should be thought of as a necessary-evil. You’ll have to occasionally add a comment to your code, but you should try your hardest to avoid them.
A comment is an indicator that you couldn’t properly express yourself in the code.
Therefore, if you think you need to write a comment, first try to see if you can express yourself in the code itself.
You might be wondering “why are comments bad?”
In fact, many developers seem to think that more comments are always good.
Here’s why comments should be avoided...
Programmers can’t maintain comments
Your code is constantly changing and evolving. It’s getting refactored and new functions, classes, etc. are being added in.
When you refactor a piece of code, how do you know if you just invalidated a comment written somewhere else in your codebase?
Unless you keep track of every comment that pertains to the piece of code you just refactored (impossible to do if you have tons of comments), then you have no way of finding out.
Your compiler will tell you about errors in your code, but there is no way of knowing what comments in your code are incorrect/misleading.
Also, as you refactor your code, comments tend to get separated from the code they describe and eventually become blurbs. And as time passes on, the code they describe may change and the comment will become inaccurate.
The older a comment is, and the further away it is from the code it describes, the more likely it is to be just plain wrong.
Figuring out which comments in your codebase are wrong/misleading is an extremely difficult task.
Too many comments will make readers tune the comments out
Another issue with excessive comments is that eventually, the reader will start to tune them out.
If your code properly expresses itself but you still place redundant comments that just repeat what the code does, then the reader will learn to ignore the comments.
The comments aren’t conveying any new information, so what’s the point of reading them?
This becomes an issue because the reader will tune out important comments as well.
There’s no way for a reader to distinguish between which comments are important to read and which comments are redundant.
Comments cover up messy code
Many times, a programmer will write bad code and then try to cover up for it with a comment.
A common scenario is a comment next to a variable declaration, where the comment explains what the variable does. This just means that the variable was poorly named. You should be able to tell what the variable is doing based off it’s name (don’t fear long variable names!) and you shouldn’t need a comment to figure that out.
Another scenario is a comment next to a function, explaining what the function does. Again, this means you should refactor the function. It’s probably too long (functions should be 5 to 15 lines long) and it might be doing multiple things (functions should only do one thing)
But again, not all comments are bad.
Here are some scenarios where comments can be helpful…
Good Places to add a Comment
Legal Comments - copyright and authorship statements may be necessary and reasonable things to put at start of each source file.
Explanations of Intent - Expressing intent in your code can be quite difficult. Therefore, brief comments that explain design choices could be useful.
Warning of Consequences - A comment that warns a developer about a specific action or change can be useful.
An example is if you have a test condition that takes a really long time to run.
A comment warning the reader that the test condition takes a long time to evaluate could be useful (if there’s no way of telling from reading the code).
Amplification - A comment could be useful for amplifying the importance of something.
This ties back in to why you shouldn’t use redundant comments (comments that just restate what the code does).
If you have an excessive amount of redundant comments, then programmers will tune out all of your comments, including comments that amplify important information.
Docstring for Public APIs - JavaDocs or Docstrings can be very useful for Public APIs. But again, just make sure they don’t become outdated or misleading.
Quastor is a free Software Engineering newsletter that sends out summaries of technical blog posts, deep dives on interesting tech and FAANG interview questions and solutions.
Tech Snippets
How Have I been Pwned got pwned by cloud costs - Troy Hunt is a software architect who teaches/writes about security engineering. He is the creator of the Have I been Pwned website where you can check if your personal data has been leaked in any past data breaches. It’s super useful and the site gets millions of users every month.
The website was recently hit with an unexpectedly high cloud bill from Azure for $8,000. The high price was due to a misconfiguration in Cloudflare that caused them to stop caching a 17.3 gb zip file and instead request it from Azure storage (making Troy pay massive data egress charges).
Troy goes through how he figured out the issue and the mistakes he paid that caused the massive bill.
Set Cloud monitor alerts for usage spikes
Configure a cloud budget and set cost alerts for when you exceed that budget
TypeScript Features to Avoid - This is an interesting blog post that lists a couple of TypeScript features that you should be wary of / avoid. Read the full post for a detailed explanation on why for each of these features.
Avoid enums
Avoid namespaces
Avoid decorators (for now)
Avoid the private keyword (for now)
Go’s Most Important Feature - When asked about Go’s most important features, many folks will talk about Go’s simplicity, C interoperability, compile speed, etc.
However, Go’s best feature (according to the author) is the ability to write asynchronous code while doing so in a synchronous interface.
The author gives an example of an HTTP request made in Go vs. Java to show how Go can schedule work very efficiently.
70% of YCombinator Startups Offer Remote Work Options - YCombinator is a startup accelerator that has helped launch some of the biggest startups in the World.
They have a Work at a Startup platform where you can create a single profile and use that to apply to all of YCombinator’s companies.
The platform has seen a massive shift in remote-friendly jobs over 2021. There’s been a 6.4x increase in remote jobs and now.
In 2019, just 15 percent of small companies and 10 percent of large companies on the platform were building remote organizations.
In 2021, that shifted dramatically to 86 percent of small companies and 85 percent of large companies.
The Ultimate Guide to Browser-Side Storage
If you need to store data with guaranteed persistence, then you’ll need some type of database in your backend.
If you don’t need a persistence guarantee, then sometimes storing data client-side (in a user’s browser), is a more viable option. Other times, it may be essential to use browser-side storage.
The article linked is a comprehensive guide to your choices for browser-side storage.
It talks about
Current Options - APIs you can use today in all modern and most older browsers
Future Options - APIs that don’t yet have full browser support
Past Options - Deprecated APIs you should avoid
Here’s the summary
Current Options
Web Storage
The Web Storage API allows you to store key/value pairs.
You can have localStorage for persistent data and sessionStorage for temporary session data.
The browser limits each domain to 5 megabytes and read/write operations are synchronous, so they can delay other JavaScript processes.
Web Storage is also string-only, so you may have to serialize/deseralize your data.
Additionally, saving large datasets can affect page performance (because operations are synchronous).
IndexedDB
The IndexedDB API is meant for storing large amounts of structured data.
The data storage limits are quite large (if they exist at all), but the various browsers handle limits and data eviction differently.
IndexedDB gives you a NoSQL-like key/value database where your values can be complex structured objects.
Each IndexedDB is unique to an origin, so it cannot be accessed by any other origin.
The API uses database indexes to enable fast searches of your data (hence the name IndexedDB).
The API is mostly asynchronous (you have to pass callback functions) and it is built on a transactional database model. Transactions are atomic but IndexedDB does not provide transaction isolation guarantees (for concurrent transactions in multiple tabs).
The main disadvantage of IndexedDB is that the API is quite poor.
However, you can use a wrapper like idb to make it usable (which is what the majority of developers do).
Cookies
Strictly speaking, cookies are not a client-side storage option since both the browser and the backend server can modify a cookie.
But, cookies are one of the most popular browser-side storage option and they’re essential for any system that maintains server/browser state such as logging on.
A domain can store no more than 20 named cookies with a maximum string of 4 kilobytes in each.
This is a restrictive 80 kilobyte limit, but that’s because every HTTP request and response sends the cookie data.
However, some disadvantages with cookies (other than the limited storage space) include
Require string serialization and deserialization
Browsers and plugins can block cookies
Legal requirements (you may need an opt-in or warning)
Cache API
The Cache API stores HTTP request and response objects.
It’s primarily used for PWAs to cache network responses so apps can serve cached responses when they’re offline.
The Cache API is not really practical for storing other types of data.
Chrome-based browsers typically permit 100 megabytes per domain, but Safari limits it to 50 megabytes and evicts the cache after 14 days.
Past Options
WebSQL
WebSQL brought SQL-like database storage to the browser, allowing anyone who knew SQL to use it.
However, Chrome and Safari offered varying inconsistent implementations of WebSQL, while Mozilla and Microsoft opposed it in favor of IndexedDB.
The API was deprecated in 2010.
AppCache
AppCache is the predecessor to the Cache API.
It attempted to specify caching behavior in a plain-text manifest file, but there were many issues and gotchas around AppCache that would break your website.
A great article that goes over the issues with AppCache is the aptly titled Application Cache is a Douchebag by Jake Archibald.
Check out the full article for a lot more detail and code snippets!
Interview Question
You are given an M x N
matrix where each row and each column is sorted in ascending order.
Write a method to find an element in the most efficient way possible.
Here’s the question in LeetCode.
Previous Solution
As a reminder, here’s our last question
Given an integer array nums
and an integer k
, return the number of good subarrays of nums
.
A good array is an array where the number of different integers in that array is exactly k
.
For example,
[1,2,3,1,2]
has3
different integers:1
,2
, and3
.
A subarray is a contiguous part of an array.
Here’s the question in LeetCode
Solution
This question is asking for the number of subarrays which contain exactly k
different integers.
In order to make the question easier to solve, we can slightly reframe it to asking for the number of subarrays which contain at most k
different integers.
Then, we can return the answer by solving the following equation
exactly(k) = atMost(k) - atMost(k - 1)
We can solve the atMost
function by using the sliding window technique.
We’ll use a Counter to keep track of the sliding window and iterate through the array, adding an element to our sliding window each time.
If the number of distinct integers in our subarray is more than k, then we’ll remove elements by incrementing the start of our sliding window.
Finally, we can add the number of subarrays that contain at most K distinct integers to our total tally.
Every single subarray that ends at the windowEnd index will contain at most K distinct integers, so we can just count all of them.
Counting all of them is the same as counting the number of elements in our current sliding window. (If you’re confused about this, here’s a longer explanation)
Therefore, we can easily calculate the number of subarrays with windowEnd - windowStart + 1
Here’s the Python 3 code
Credits to lee215 for the elegant solution. My original solution was nowhere near as concise lol.
Despite its cult status, Clean Code's advice is continuing to become more antique. John Ousterhout's "A Philosophy of Software" is much better, and not-so-subtly refutes Clean Code on a number of issues, including comments. (The book has its issues too, but it's a worthwhile read)
Loved this one. Thanks!!