Readers of this blog will know that I have been doing a lot more coding lately. I play the role of a technical writer at work, and so I do a lot of coding there, but in my spare time I have been trying to work on more "weekend" projects. This week I decided to take on a challenge: to build a news aggregator using NextJS and GraphQL. Although I am familiar with both technologies, this is the first time I have used them together. I have had a good experience with these tools -- they are my bread-and-butter -- and it was exciting to see how they interact using React Apollo.
While I was reflecting on the progress I have made so far, I started to think to myself what principles underlie my coding. I am not an expert programmer, so I am not familiar with all modern software principles. This means that I have had to look within to establish what practices I like to use when I am coding. I have decided to write about this topic to share with you, my readers, how I think when I'm coding in Atom and Hyper. I should say that this post is based on a somewhat limited sample size as my reflections are mainly on how I have coded lately, but I know I need to write these down on paper.
The first principle I have been embracing in my programming is privacy. It's no secret that I care about the independent web. I've written before about the impact that "silos" have had on our internet, and the outlook is not promising. Power concentrated in the hands of the few is almost never a great situation to be in. Google controls much of what we see on the internet. Facebook has a massive advertising engine that influences how we experience websites.
I think that in order to be in support of privacy, you have to incorporate privacy into your work and life. After much deliberation, I recently switched over to ProtonMail. (I discovered that ProtonMail had an Apple Mail bridge, which made the transition so much easier) But that's not enough for me. I don't just want to take back control of my privacy. I want to ensure that the people who use what I build are given the same experience that I am coming to expect from the platforms that I use. That is why I like to think about the privacy of a user when I am writing code.
This blog does not contain any analytics packages. Don't believe me? You can check out the source code on GitHub. I don't use any heatmap-tracking technologies or anything like that, either. If you look at the source code, you'll find that I seldom reference external APIs. I use the Airtable API for my "cup of tea" counter on the homepage, but I think that's mostly it (correct me if I am wrong, please). When you come to this website, I don't want you to have to download megabytes of data to view it. NextJS may be a bit more verbose than plain-old HTML and CSS, but I am happy with this trade-off because NextJS compensates with its developer experience and integration with frameworks like React.
As I have been building Intern News, the aforementioned news aggregator, I decided that I wanted to make privacy a central part of the platform. I don't ask the user for any information that the platform doesn't need. There isn't even a field to insert your name; you can only submit a username to Intern News. Why do I need to collect this data if it is not going to enhance the user's experience on the platform? I also decided that I don't need to call any external APIs. I could have used a few APIs like Algolia for search, but I didn't need to. I'm sure that many of the APIs I would have used have strong privacy practices, but there's no getting around the fact that as soon as you use an API, your user's data goes somewhere else.
The guiding principle that's been going on in the back of my mind is simple: if I would not be comfortable with this feature on a site I use, then I should not add it to what I am building. It's up to developers to help safeguard privacy on the internet.
I like to write simple code. As I just discussed, I decided not to call any external APIs using Intern News. It wasn't just because I cared about the user's privacy that I made this decision. I decided that external APIs would introduce too much complexity into a platform that could be a lot simpler. Why would I use an API that would only marginally improve the user's experience if: (i) it would require me giving up data to another platform; and (ii) it would require more work on my end to maintain the codebase.
Maintainability is not something that I thought a lot about when I was learning how to code. I don't know much about other people's experiences learning how to code, but I am going to assume that most beginner developers don't think about the longevity of their codebase. When you're getting started, what matters is that you are writing code. Once you have learned the basics, then you can worry about how to enhance your codebase and make it easier to maintain.
Writing simple code makes my code a lot more maintainable. I know that programmers are capable of holding large codebases in their head at once, but think about how much easier it is to maintain a platform if you have a smaller codebase.
There are a few ways in which I ensure the simplicity of my code. First, I like to reduce redundancy whenever possible. I'm going to write a blog post on how I structured my GraphQL queries on the front-end of Intern News at some point because it was one of my break-throughs yesterday, but I'll give you a summary now. In short, there were many different queries that I was reusing throughout my codebase. I defined each query I was using on the page, because that made it easy for me to test each query. Then I wondered: why should I repeat these queries if they are the same? I decided to create a new folder structure to store my queries and mutations so that: (i) I was not repeating code; and (ii) each component was easier to read.
The result was a vastly simpler codebase. I don't need to scroll through multiple queries and mutations before I see each component in my front-end source code. I can now change the queries and mutations by going to their own file. It makes things so much simpler.
I also decided to start using a linter on my front-end code. I'm still quite new to using linters, and it looks like there's a lot of ways in which you can configure them, but to start I used eslint with the standard configuration. This helped me find a number of instances where I was importing packages that I was not using. I've uncovered variables that I declared that are not referenced. I find this is the consequence of copying code from one file in your codebase into another; you forget to check whether you need all of the code you have copies. Eslint has so far allowed me to remove a number of library references and variables. Again, this makes the codebase simpler.
Simpler code is easier for me to read and maintain. Less is more. I must admit that the codebase at the start of my projects is often messy, but that's because I like to get into building the core features first. I come back to optimization later, once I've developed the initial structure for an application.
Last night, I started thinking about new side projects that I could build. I came up with a few good ideas, and I hope to explore one or two of them soon. But I didn't need to start thinking about new side project ideas just yet. There's still a lot of work for me to do on Intern News.
If you've been coding for a while, you will know that lines-of-code is a bad metric of progress. Fixing a tough bug could involve hours of reading over source code and result in only changing a few lines. More commonly, you'll write a few different solutions, compare them to figure out which one best fits your needs, and then delete the code from your other solutions. It just doesn't make sense to track your code based on lines written.
I suffered from a similar problem yesterday, but masked in a different way. Most of the main features of Intern News are done, so I could move on to another project. Then I started to think about what I could do to improve the project. It's important for me to say that by "improve" I mean enhance the user experience or make the codebase prettier. I don't mean "improve" as a synonym for add features in just for fun. My thinking along these lines resulted in a number of discoveries. I could use a Rails mailer to create a "forgot password" feature. It's not a major deal, but in my experience I have mainly used SendGrid to send emails. Using a Rails mailer would be a good learning opportunity. I also thought about adding pagination.
These are all features that would make the platform better, but they may not exactly require writing a large number of lines of code.
There is a cycle when you're working on a new project. First, you start to develop the initial structure for the application. At this point, a lot can change; you just want to nail down the basic architecture so you can get down to building the main features. Next, you write a lot of code that implements the features you want to see. At this stage, my progress is really easy to see. So many new lines of code have been added to my codebase. In the case of a front-end application, I can visually see the progress I have made. It's tempting to stop here, but there is still another stage in the cycle: optimize your code. This is the stage I am at with Intern News. I am looking for ways to reduce redundancy and build a better user experience.
Optimizations may not be as "sexy" as writing in big features, but without those optimizations I will never be able to write my best code. "Good" is sometimes enough. That's something that I learned a long time ago in software engineering. (One interesting aside: A friend of mine who attends a lot of hackathons told me that they skip building the back-end for authentication and create a dummy front-end. It reduces time spent building authentication, which every developer with some experience building auth services knows can be an arduous task.) But in my case, I want to write my best code as much as I can. I want to see my code as if it were art. The inner-workings of how I built it may be messy, yet I think all artists feel that way about their work. It's the final product that counts.
These are the three principles that have been driving my software development work lately: privacy; simplicity; and optimizations. I could have made this post a lot longer, and written about industry-standard principles like Don't Repeat Yourself (DRY), but I deliberately chose not to for two reasons. I don't have enough experience building professional software to talk about the "best" principles, and I am also still exploring what principles matter most to me. For instance, I'm still thinking through my principle on APIs. Should I build publicly-accessible APIs for my projects? I'm also thinking a lot about documentation. Should I write documentation for my side projects, even if nobody is going to use them?
I've had a lot of fun building this blog and Intern News. That's all I care about. These principles do not act as barriers for my development. They help guide how I write code. I would say that I'm only really thinking about these after writing code. In the thick of it, I was too busy building features and solving bugs to reflect on what it was that mattered most to me. I find this is easy to do, because, at least for me, coding requires most of my attention.
How you build good software is up to you. It depends on what your developer workflow looks like. For me, privacy, optimizations, and simplicity are important, but you may have other priorities. It's not like you need to worry about these if you are just getting started, but I'm hopeful that having these principles written down will hold me accountable for the quality of my code, when I need that accountability. My code is all open-source anyway. If you ever have any feedback -- or want to contribute to something I have built -- you can do so on GitHub.
Editor's note: I have been using subheadings in this post for perhaps the first time on this blog. What do you think?