Skip to main

Making this website

Or how to reinvent the wheel

This year I have finally reached a point in my career where I can put the word "senior" on my resume. It feels good, but it also made me seriously consider what I should do next. I have decided that now would be the perfect time for me to focus on my personal brand. And making my own website would be just the ideal starting point to do so. I wanted to create a website that would act both as a portfolio of my projects and as my personal blog. I could've picked Wix or Squarespace or any other website builder to get it done "quick and dirty," but that didn't feel right to me, not "professional." Maybe I'm overthinking this a bit, but I believe that as a developer I'm expected to actually develop my own website. So something like Gatsby + Vercel might be the obvious choice here, right? Those are great in their own right, and I'd most likely pick either Next.js or Gatsby for my next web project, but it still didn't feel hands-on enough. Being a mobile app developer, I wanted to use this experience as a learning opportunity. I wanted to get in deep into the nitty and gritty details of web development. I wanted to step on every rake out there and cover all of the nuances. So I've decided to build both the frontend and backend from scratch. Also, I didn't want it to be too simple, so I've decided to add a barebones CMS functionality. I wanted to have the ability to add and edit articles most seamlessly and conveniently, so I've decided to add a secret article markdown editor. Here I'll briefly go over how it all went.

The frontend

I've been working as a React Native developer for the past 3 years, so choosing a framework was a no-brainer (React.js). I've also been using Typescript for years, and quite frankly I can't imagine a big js project without it. It's powerful, it's easy to work with, it's just absolutely great all around. For me, it's the default for any js project, so of course, I used Typescript here as well. I already have some experience with Webpack and building up a React app from scratch, so I've decided to cheat a little bit, skip the basic setup, and just use create-react-app. And boy am I glad I did that. The experience has been just perfect. I had absolutely no issues with it whatsoever except for one, but I'll get into details a bit later. What I wanted to avoid is using Redux. I have some pretty serious gripes with this framework, and I'm convinced that it's not cut out for every project out there. More so, I'd go as far as to say that only a very specific category of apps/websites works really well with Redux. I will elaborate on my argument in detail in the upcoming articles. I also won't go too deep into justification and explanation of the dependency injection pattern approach I used here but please stay tuned as I will surely do that in the next posts. The overall setup is pretty basic. React Router, lodash, luxon (since moment.js got deprecated), Helmet - you know, the basics. I've been using Sass for CSS, and it has been working out just fine. A few words on animation. Being spoiled by React Native's Animated API and Layout Animations I was pretty disappointed to find out that React web doesn't have a similar built-in tool that is as powerful and as convenient to use. Transition Groups work fine for simpler animations, but they are clunky and excessively verbose, and just aren't very pleasant to work with, in my opinion. Too much code is required to animate even the simpler transitions. Given all that, I was so pleasantly surprised to discover React Spring. It's such an amazing library, I couldn’t recommend it enough. The development experience? Incredible. The performance? Top-notch. It's all I ever wanted from an animation library and a little bit more. Simply magical.

I would've praised CRA just as much if not for one small issue – it's not that easy to change the configuration. I guess it's a part of the design, but I'm convinced it would've been so much better if developers had the ability to easily extended Babel or Webpack or linter configuration. Perhaps I'm asking too much though. I'll go through my case in detail. I'm using inversify.js for dependency injection. The library allows injecting services in a constructor, which is very convenient. Unfortunately, the implementation of this feature relies on function decorators, which have been in the proposal phase for quite a while now. CRA doesn't include proposal-decorators babel plugin out of the box, so I had to add it myself and it turned out to be not as easy as I expected. Most articles on the topic suggest ejecting the app, but I didn't want to do this. All I needed is to add this one tiny babel plugin to the configuration, nothing too dramatic. Going so far as ejecting the app just felt too scary. Especially, when there're also plenty of articles advising not to do so as well. After some research, I ended up going with rescripts (I guess customize-cra or react-app-rewired would've worked just fine as well), since it allows a fine-grain control over which parts of the config are being overridden in a very convenient way. Things did break, as was expected, but I fixed everything rather quickly. I guess I just got lucky? I would also like to mention React Loadable. It's a pretty incredible library all around. I'd say that this is one of the easier shortcuts to getting that sweet 100 points for performance in a Lighthouse report. An absolute pleasure to work with.

You might have already noticed that every time you refresh the page background pattern changes. That's because it's actually procedurally generated using Processing (p5-js, to be precise). Originally, I wanted the pattern to be animated, to spice my rather dry UI up a bit. I wanted for it to change and shift slowly so that it's not distracting but noticeable just enough. Unfortunately, animation affected the performance so negatively I had to disable it at least until I find time to optimize. My guess is that it has to do with canvas size being too big, but either way I'll have to dig deeper. For now, it basically runs only the first frame of animation on page load.

Finally, I'd like to touch a bit on how I handled forms. I didn't want to use Formik or any other library for this, so I ended up writing my own form handling. Even though it's very bare-bones in its current state, I'm pretty satisfied with how it turned out. I even think my solution might have the potential to become a full-fledged framework. I plan on writing an article exploring the concept and the implementation in detail in near future.

You can find all the sources here.

The backend

The server side turned out to be pretty basic. The only backend experience I had prior to starting working on this website was with Photon Server. I also had experience with MongoDB, although, for this project, I decided to go with PostgreSQL. Originally, I started working on this server using Golang and Echo framework, but several months in, I had reconsidered this choice. I've been a fan of Go for quite a while now, I really enjoy working with this language. However, I'm planning on creating several open source projects in the future, and I want as many people as possible to be able to contribute. Unfortunately, Golang isn't the most popular language out there, and even though it's a general-purpose language, it only truly shines in high-load backend services. Considering that some of my future projects will include ML, I had decided to completely rewrite the entire backend in Python. Even though the server was about 70% complete by the point I made this decision, I have absolutely no regrets. I gained lots of hands-on Python development experience that would be immensely useful for my future projects. The transition went smooth for the most part. Here, I'd like to note just how incredible Viper for Go is. Probably the best configuration framework I've ever worked with, really missed it in Python. In fact, I missed it so much that I couldn't just bring myself to use any other configuration library! I've decided to write my own configuration manager using PyYAML and watchdog. The part that probably took the longest to transition is database interaction. I'm using SQLAlchemy and it's been pretty great so far, but the learning curve is rather steep. I'm also using FastAPI, and it's an incredible framework all around. I'd even go as far as to say that this is probably the only Python server framework you should care about. Thoroughly recommend.

I've also been experimenting a bit. I'm pretty sure this is unconventional, but I treat article icon SVGs as strings, rather than files. I store them in the text type column in the database. Frankly, I still have no idea if this is dumb or genius.

You can find the sources here.

Things to improve

  • Custom server-side analytics. This website respects your privacy by not storing or collecting any data. I don't want to use Google Analytics or any third-party tracking systems on both client- and server-side. Still, it would be nice to gather some analytics data like how many views each article gets for example.
  • RSS, or a newsletter. Does anyone still use RSS? Does anyone actually read newsletters?
  • Ability to preview image files currently available on the backend. Basically a remote file manager UI.
  • Pretty previews for messengers/social networks. You know how when you share an article via Telegram it displays a neat preview instead of just a link? It would be nice to have something like that.
  • Add search. Sooner or later, I'll have so many articles that a search function would be absolutely crucial. I'm pretty excited to get a hands-on experience with SQL full-text fuzzy search.
  • Add a tag system, where each article would have a set of tags, and users would have the ability to view all articles for any particular tag.
  • Use preact.
  • Some minor tweaks, fixes and improvements here and there.

In conclusion

I'm really glad I decided to build this website without relying on frameworks like Gatsby. It took me almost 9 months to get here, given that I have been working on this website only during my free time, and I had to learn a lot of new frameworks and patterns. I've gained so much knowledge and so much priceless experience along the way, I have absolutely no regrets.

Feel free to use my code for your own personal projects, and let me know if you have any ideas or suggestions. You can reach out to me using email.

UPDATE 🚀

It's been about a few months since I've written the original post, and this website has evolved quite a bit since then. The most important change – I've switched to Next.js. How did this happen? Well, originally, I was planning on using react-snap to generate static pages to improve my SEO. However, I've ran into several serious problems. Nothing wrong with the package itself, it just didn't quite work out for my usecase. First, a few words on how react-snap works. It uses Headless Chrome to crawl all available links starting from the root, runs javascript on every page, and saves generated DOM HTML as a statically served file in their respective folders according to their path. This process has to be triggered manually, preferably during a build. Which, of course, brings the question - how do I generate static pages for articles added via CMS? I tried the most straightforward approach – I decided to rebuild the website and run react-snap every time a new article is added. Initiating the process of rebuilding the client, as well as moving files around was handled by the python backend, while actual rebuild and react-snap static page re-generation were handled by yarn build command. I've managed to make it work, however, I was very dissatisfied with how everything turned out. First, it was quite tricky to set up, since it requires the machine running the backend not only have all backend python dependencies installed but also everything required to build the client as well. This isn't very scalable. More than that, it just felt very "hacky". Second, serving my react pages statically brought a whole bunch of various problems with it. The UI suffered the most, it became very janky and weird. Even though when the HTML is loaded by browser it already contains a full "pre-rendered" page, the javascript is still executed. I was using loadable on many pages and it has been the main source of problems. The users would see the page, then it would blink, and reload again awkwardly as loadable would load the required code chunk. And it wasn't the only source of issues. All in all, this just made me realize that this approach is simply not worth it. If I'm going down this route, why not just completely remake the entire client to use SSR/Static pages, and not waste any time on half-measures? This is how I've ended up switching to Next.js. Despite this being a very serious endeavor, somehow I've managed to pull it off in just one month. I was learning many things about Next.js along the way. If I were more experienced with it, it would've probably taken me even less time. I've also updated my backend setup. Now, my website is hosted by Vercel, and the backend is split between 2 machines – one hosts the DB and the other the service. In the end, even though my website is still far from perfect, I'm very proud of how it has turned out. I believe I've managed to lay down a framework that's both flexible and powerful and could be reused in my future projects.

Thank you very much for reading! 👋