tadasv site

My difficult SaaS bootstrapping journey

This is going to be my second year running Simple OKR. The beginning of a new year is usually the time when I start collecting all business income and expense data and start filing various LLC paperwork with the state.

I thought I’ll share how much I made this year from Simple OKR.

Category Sub category Amount
expenses ads -1100.87
gcloud -46.02
gsuite -78.36
legal -55
office -67.67
software -348
expenses Total -1695.92
income stripe 14616.13
income Total 14616.13
Grand Total 12920.21

It’s only ~15K USD in annual revenue. Of course I’ll have to pay ~25% in taxes on that grand total number, which will probably gonna bring me down to ~10K USD in profit. The ARR is about 10% increase from the previous year. It’s a positive change, but has very little meaningful impact on my life.

It’s really hard to launch a successful SaaS on your own. I’m kinda surprised that I’m making any revenue at all. I’ve been building Simple OKR as a “side-project” for over 2 years. I remember quitting my full-time job in mid-2019 when I started seeing steady increase in MRR (~1.3K) and I thought I should focus on it full-time. Unfortunately, after I quit my job, the MRR started going down (to ~600 USD) and I did not bring in any new customers for about 3-4 months. That was a really depressing time for me and I decided go back to work full-time elsewhere. Looking back at this, I probably made the right choice to get a job. I tried to move too fast and burned.

Hmm, my major mistakes… I think there are several things I can think off.

First off, technology choice. Simple OKR is built in Go and React JS. It did not start like this. It went from simple server side rendered app to React JS frontend. I’m fairly happy with Go as a programming language, but I think I should have stayed with SSR instead bringing in React. Bringing in react just meant that I had to build out an API (complexity++) that can be used with React. It does not make HTML or CSS parts that much simpler or easier to reason about. I was still able to make a fairly big mess out of React components (complexity++).

Second issue… UI redesign 3x. I redesigned the UI of Simple OKR probably 2 or 3 times. Of course, the goal was to improve user experience and make Simple OKR for people easier to use and reason about. I’m not sure I was successful in achieving that. You can see it’s still clunky and difficult to use (waistedTime++).

Third mistake was to try and add another product to the main offering. Let me explain. I started building out a new product to complement Simple OKR – Performance Reviews. The goal was to bring performance reviews from your manager and peers and tie those back to OKRs somehow. I wasted about 2 months on this and did not see much interest (waistedTime++). I still think it’s a good offering, but only when the time is right.

I think these are probably the three big mistakes that come to mind. Mostly, all of them revolve around wasted effort on stuff that does move the needle.

Not everything was that bad. On the good side, running ads early on was a great decision. When you bring something new to the market, nobody knows you. You need to have distribution channel for your software so it gets discovered. Google ads was that channel for me. I still pay ~80USD/month for google ads just to maintain the foot traffic. Though, it’s not very sustainable approach unless you have tons of cash to burn. Though, I think without buying ads I probably wouldn’t have gotten to the current MRR at all.

At the moment I’m not entirely sure what I want to do with Simple OKR. Should I continue cleaning it up and making it better. Or should I just leave it and let it die on it’s own. I know there’s still lots of potential for this product.

Making s-expression evaluator

For the past few weeks I’ve been working on a new personal project. I want to build an experimentation platform that lets you easily setup and run various experiments on the web. One key component of this project is to be able to encode experiment configuration as data instead of implementing all the rules in code. Having configuration in data makes it easier to change and update experiments on the fly without having to rebuild the app.

I started developing Brogue parser and evaluator. Brogue as in accent, not the shoe. Brogue expression syntax is based on the idea of Lisp’s S-Expressions. However, it’s encoded as JSON object (so it can be more easily parsed and understood by the frontend code if needed). Let’s look at this example:

{
  "if": {
    {
      "lt": [
        {"hashmod": "user-1", 100},
        10
      ]
    },
    "Sign up for 14-day trial",
    "Start free trial now"
  }
}

Example above is a valid Brogue expression. Once evaluated it will return either “Sign up for 14-day trial” or “Start free trial now”. The example above uses 3 functions: if, lt and hashmod. In this example, hashmod takes a hash of value “user-1” and mods it by 100. Then passes it to the lt function which checks if the mod is less than 10 or not. If it is smaller than 10, we will return 14-day trial message, otherwise we will get “Start free trial now” back.

Where this gets really interesting is that you can actually supply external information when evaluating the expression.

context := json.RawMessage(`{"user_id": "user-1"}`)

evaluator.Evaluate(context, `
{
  "if": {
    {
      "lt": [
        {"hashmod": {"context": ["user_id"], 100},
        10
      ]
    },
    "Sign up for 14-day trial",
    "Start free trial now"
  }
}
`)

This example is actually equivalent to the first one. It will produce the same result. However, now the user id is passed from some external source instead of being hardcoded in the rules. This open up lots possibilities for constructing complex rules that produce different results once the operating context changes. Brogue is also extensible. I am able to add new expressions very easily by implementing a Go function and binding it to some function name such as “and”.

At the moment Brogue is implemented in Go and is still in the works. I may open it up once it’s complete and I have more concrete cases.

FreeBSD writable live USB

FreeBSD installation images come with “Live CD” option. If you choose it, the installation image will boot in live cd mode. This is really useful if you want to explore core system from the command line or do some fixes on already installed system.

If you want to install more applications to have a better test drive you’ll be in trouble because root file system is mounted as read-only. The root file system also takes up 100% of space due to how installation image is setup leaving you no room to add new software.

There’s a very simple workaround that lets you put FreeBSD live cd into writable configuration. Boot up into usb image and enter live cd. Then run these commands:

mount -uw /
touch /firstboot
# edit (vi) /etc/fstab and change ro to rw
sysrc root_rw_mount=YES
sysrc growfs_enable=YES

Reboot the system when you’re done. When you boot it up next time, your root will be writable and will expand to the remaining free size of your USB stick.

X11 clipboard synchronization

Clipboard in Linux was always a PITA as far as I can remember. We have three clipboards to work with in X11: primary, secondary and clipboard. Primary buffer is typically used in terminal apps (e.g. when selecting text with mouse), secondary not sure, and the “clipboard” is used by GUI applications such as you web browser and such.

These clipboards are not automatically synced by default. For example, if you select console text and try to paste it in your browser it won’t work out of the box. You can transfer primary buffer context to clipboard with xclip like so:

xclip -o | xclip -selection c

Then it will work. But who wants to do this allthe time.

There’s a program called autocutsel that can help you with clipboard sync. You just need to install it and add the following to ~/.xinitrc:

if [ -x /usr/bin/autocutsel ]; then
    # keep clipboard in sync with primary buffer
    autocutsel -selection CLIPBOARD -fork
    # keep primary buffer in sync with clipboard
    autocutsel -selection PRIMARY -fork
fi

It will start autocutsel in the background when X starts and keep clipboards in sync.

I wrote a custom static site builder for my website

After I decided to get back into blogging and hosting my own site I started looking for the right approach and tools for creating my site and blog.

Instead of picking up an existing blogging platform (such as Wordpress) or some static site generated (e.g. Hugo) I decided to roll my own tool for building this site. Some people probably think that I’m crazy for doing that, but I think it’s the right choice; at least for now. Here’s why.

There’s a steep learning curve to any new platform that you want to start using. I’m not proficient in Wordpress, plus it adds more complexity to hosting (I need a DB). Same holds true for static site generators. I previously used Hugo, it’s great, but again there’s a learning curve and I need to adapt myself to their approach of doing things.

I decided to roll my own solution. I wrote a simple static site generator in Go (as of this writing it’s implemented in under 200 LOC). I take markdown files, convert them to html and produce this site. It’s really simple. The nicest thing is that I have full control over all aspects of site generation. I don’t need to learn how to use some existing tools, I already know Go and that’s all I need. We’ll see if I’m going to regret this approach or not, but so far it seem much superior to anything I used before due to its simplicity and flexibility.

Issues with Arch Linux

I’m an avid Linux user and I’ve used Linux for probably over a decade in some capacity either at home or professionally.

At the end of 2018 I got myself a new desktop computer and decided to make Linux my primary driver at home. I mostly do development work on a computer anyway, so it lends itself very good to that. I decided to install Arch Linux since this is what I used many years ago and I like its minimalistic nature and up to date packages.

Unfortunately, over the past couple of years I kept experiencing kernel crashes. It’s still unclear to me why I’m getting these system crashes. Initially I thought they were related to hardware being “too new”. However, I still experience the same crashes two years and many kernel upgrades later. The crashes don’t happen often, but I noticed that I typically get them during systemd upgrades. If I get a crash during systemd upgrade it will most likely leave the system in a bad state, requiring me to manually interfere and fix things from a bootable USB. This is really bad because now I’m hesitant to perform system upgrades.

Here’s some lspci output for the reference:

00:00.0 Host bridge: Intel Corporation 8th/9th Gen Core 8-core Desktop Processor Host Bridge/DRAM Registers [Coffee Lake S] (rev 0a)
00:02.0 VGA compatible controller: Intel Corporation UHD Graphics 630 (Desktop 9 Series)
00:14.0 USB controller: Intel Corporation Cannon Lake PCH USB 3.1 xHCI Host Controller (rev 10)
00:14.2 RAM memory: Intel Corporation Cannon Lake PCH Shared SRAM (rev 10)
00:14.3 Network controller: Intel Corporation Wireless-AC 9560 [Jefferson Peak] (rev 10)
00:16.0 Communication controller: Intel Corporation Cannon Lake PCH HECI Controller (rev 10)
00:17.0 SATA controller: Intel Corporation Cannon Lake PCH SATA AHCI Controller (rev 10)
00:1b.0 PCI bridge: Intel Corporation Cannon Lake PCH PCI Express Root Port #17 (rev f0)
00:1c.0 PCI bridge: Intel Corporation Cannon Lake PCH PCI Express Root Port #1 (rev f0)
00:1d.0 PCI bridge: Intel Corporation Cannon Lake PCH PCI Express Root Port #9 (rev f0)
00:1f.0 ISA bridge: Intel Corporation Z390 Chipset LPC/eSPI Controller (rev 10)
00:1f.3 Audio device: Intel Corporation Cannon Lake PCH cAVS (rev 10)
00:1f.4 SMBus: Intel Corporation Cannon Lake PCH SMBus Controller (rev 10)
00:1f.5 Serial bus controller [0c80]: Intel Corporation Cannon Lake PCH SPI Controller (rev 10)
00:1f.6 Ethernet controller: Intel Corporation Ethernet Connection (7) I219-V (rev 10)
03:00.0 Non-Volatile memory controller: Micron/Crucial Technology P1 NVMe PCIe SSD (rev 03)

My CPU is Intel(R) Core(TM) i7-9700K CPU @ 3.60GHz.

Recently I’ve been contemplating switching to FreeBSD. I’m hoping to get more stability from the system at the expense of losing certain software (though I don’t need much). My biggest blocker at the moment is the lack of Docker for FreeBSD. It’s something I need for certain dev workflows and publishing my own software to GCP. Though, I wonder if I should be able to get around it with bhyve and VMs for docker where I need to build images.

When to build software from scratch?

When to build it from scratch from a business perspective? This is a complex question, but I’m going to give you a simple answer.

In an ideal world, with unlimited time and resources, you should always build your own software. This will give you full control over features and life-cycle of the software.

Now, of course the reality is that we don’t have unlimited resources. Then, we should build software that’s core to our business. Don’t waste time on implementing low level libraries and frameworks if that’s not core of your business.

Over the past decade in software engineering I learned that you want to start by owning the core of your business with the intention to replace the rest of the software with in house solutions. Off the shelf solutions are great initially since they give you speed and a way to test things out, but at the end you will always want more control, which can only be achieved by owning the software completely. Very few businesses will end up there.

Own what’s core to your business with the intention to own everything eventually.

Why blog?

What is writing about? Why do people share things online and blog? It is much easier to answer this question when one has picked a theme they want to write about. If you would like to focus on technology and gadgets, then it’s easy to say that you’re writing because you want to discover and share interesting things about the latest technology. That could be one example.

For me it’s a bit different. I don’t really have a clear theme, goal, or topics that I want to explore. I think personal blogs should not have themes at all. Themes are great to focus your efforts in order to produce quality blogs that cover certain topics, but at the same time they limit you and your ability to express whatever is on your mind. The freedom to express yourself is essential to creating a long lasting personal blog. The ideas, beliefs, interests and many other things change over time which also means that the theme of your blog should also evolve.

OK, so what and why this blog? This is going to be my 3rd (?) attempt at a personal blog. I don’t want to pick a specific theme or topic. Previously I tried focusing on tech and engineering. This time I’m planning to write about whatever I find interesting without being bound to any topics or themes. It will probably lean toward tech topics, but time will tell. I doing this because I think I need to have some personal space online.

Enjoy.