Saturday, 21 December 2024

Project 2 - Simple SAAS - Business Model

 

In the previous Project 2 post, I mentioned that one of the key outstanding design items was to come up with a business model for the site. I now have an idea of what I want to do, and this post I'll try and explain it.

The model is actually pretty simple:

The site will be based on a freemium model where all members will be able to post up to 5 articles for free. However, free articles will be (subtly) watermarked when then are viewed.  Something like this:





Subscribed users will be able to permanently post more articles ( 20?), and their published articles will not be watermarked as long as their subscription is active.

This model is simple, but it does hillight a couple of consideration points for a freemium business model

  1. What features do paid subscribers get that free users don't
  2. What happens when a subscription is cancelled. How do you gracefully remove the paid features when a subscription expires? 
In this case, I've chosen to watermark the pages for 'free plan' users, and to regress subscribed users to this model if they allow their subscription to expire. This should enable a few things

  • The site will be useable for free users, giving people an incentive to use the site as a free service
  • There will be tangible benefits for subscribing and maintaining a subscription, which should lead to people signing up for a subscription service assuming that is is appropriately priced.
  • The penalty for letting a subscription lapse will be minor - all paid published articles will be permanently available, but will simply incur a watermark if the subscription lapses.
I'm reasonable happy with this concept.  It still needs some work (how much will the paid plan be per year? how many articles will this allow? How exactly will the free articles be watermarked), but this is enough to move forwards with.

With this concept sketched out, I am ready to start integrating the site into some real world services. I'll cover that process in future posts.

 

Tuesday, 17 December 2024

Interlude - Sickness and distractions

 Ugh. I've been sick. A nasty flu and a bonus round of bronchitis because I am just that lucky. 

As such, I haven't really been focused on moving forwards with Project 2.  Instead I've allowed myself to get distracted and started a couple of other things on the side. Did I mention that one of my biggest flaws is that I am great at starting things and terrible at finishing them? I think I might have.

However, it turns out that one of the projects was something simple that I was able to finish in a couple of days. As well as being terrible at finishing projects that I have started, I am also terrible at making decisions regarding what to work one.  I  dither so much over the 'what shall I work on today' question that I often just give up on being productive and choose brain rot instead. 

To fix that, I made a simple tool to help make decisions.  https://thedecisionator.com/ lets you create a list of choices to make, and then will randomly pick a choice from that list for you.  The lists of choices are stored to local browser storage, so they will persist between browser sessions on the same device




The site is another Vue/Vuetify/Typescript site that I've deployed via Cloud Flare pages.  It is intentionally bare bones, but it gets the job done.  The next time that I need to choose what to work on, I'll be covered!

Since one of the themes of this blog is side hustles and money making, it is interesting to consider if or how this site could be turned into a side hustle.  On the face of it, there don't seem to be too many options

  • The site is too simple for advertisers to consider, it is unlikely to pass a provider review
  • There is no realistic opportunity to 'upsell' to a  paid model.
  • There isn't anything to subscribe t
About the only avenue I can think of is adding a donation link. I might look at this later.. but this would need some serious traffic numbers to be viable. This leads to marketing/traffic/seo considerations that I need to get across but which probably aren't worth it for a simple tool.

For not I am just happy to have something that I can use to help me be more decisive.  I might come back to making this a more profitable exercise later if I think of a good idea.

 



Saturday, 30 November 2024

Project 2 - UI Scaffolding progress

 Over the last couple of weeks I have been working on scaffolding the key features of my simple article publishing project, while trying to actively avoid getting distracted by constant fiddling with the fine details of UX.  As a reminder, I am trying to create a very simple, but feature complete SAAS website so that I can build a better understanding of how to integrate the back-end services like Auth, Database, Payments, Helpdesk for a real SAAS site.

In my previous post I detailed how I am using a services pattern and in-memory repositories to separate the front-end UI from these back-end concerns, to avoid having to solve too many problems at once.  This also allows me to focus on making sure that the key features of the site - which in general are the features that will attract paying customers - are valid, working and stable. This prevents losing time implementing back-end integrations for features that are in flux, or which are poorly thought out.

As a case in point,  one of the key features for an article editing an publishing site is actually being able to create and edit an article.  My initial idea here was to use a markdown style approach - users would enter wiki-style markdown which would get transformed into a page when the article was previewed or published.

By implementing the UI first, it quickly became apparent that this approach was clumsy, overly technical and would turn off a lot of prospective customers. Instead I opted to implement a rich text editor based on VueQuill, and this greatly streamlined the user experience for creating articles - which is one of the core features of the site. Focusing on the core UI enabled me to make this switch with minimal impact - as no backend changes needed to be made.

Rule of thumb for future SAAS work: scaffold the core features first. Focus on getting the stuff that users are gong to pay for working before worrying about pesky details like security, db schema, payments etc.

Anyway, here is the list of features that are more or less working:

(remember, the UX here is a work-in-progress!)

The site is a responsive layout, with a fixed width for user interaction and fluid gutters. This may change in future, but it seems to make sense for now.  

It has a dedicated members/dashboard area that supports navigation, managing articles, managing the user's account and getting help/support

Each member page is an 'invisible scroller', enabling the user to scroll content vertically without requiring browser scroll bars. Each page also has built-in feedback features, providing common notification and dialog services for page actions:



The article management features are implemented using a master/detail list pattern, where the user can create, edit, delete, publish, unpublish and view their articles



Article creation or editing uses VueQuill as a WYSIWYG rich text editor:


There is a scaffolded Account management page:


and a scaffolded support page, using a hosted support form from FreshDesk (more about this in future posts)


Published articles are available for viewing by the public:


Obviously the site is by no means complete, but the core UI features are mostly there. 

The main outstanding feature is implementing the business model for the site - adding (or restricting!) features based on user payments.  This will be my next focus, and I'll try and report progress in the next post.







Saturday, 16 November 2024

Interlude - some distractions

 A few things have been happening lately which have reduced the velocity of my current project: 

  1. I had some ideas for my 'big idea' SAAS that I wanted to spike out
  2. Liberal democracy and the rule of law apparently imploded in the United States.
  3. BlueSky took off and I've been having a lot of fun being a post goblin on a site that feels like Twitter in 2010.

The items above contributed to a motivation slump for Project 2.  There is a lot going on and it is difficult to find to motivation for building a fully fledged solution purely as an academic exercise - even if the learning potential is huge.

That being said t,  one of my key self-improvement objectives is to actually finish projects that I start and NOT give up when I hit a slump.   I will get this thing 'functionally' complete and deployed even if it is just for personal bragging rights.

Apart from the above distractions, I think there are two things killing my want to continue with this project

  1. It is basically pointless as a SAAS - the chances of making money are so low as to be non-existent
  2. UX design is really hard. I can write competent code, but even with all of the tools of Vuetify and modern CSS at my disposal I can't make it so that the website 'looks professional'.  There are just too many UX tricks of the trade that I don't seem to know. Having the artistic talents of a brick also does not help.
Item 1 I can probably cope with. Ultimately this  project 'is a learning by doing' exercise and I just need to suck up the fact that it probably isn't going to be anything else.  

Item 2 is a really an issue for me. When working on the solution I get easily distracted by the low quality look and feel and then started fiddling with things to 'improve it'.  This doesn't usually have a good outcome (ok, the end results are terrible) and wastes a lot of time.    

Moving forwards I am going to have to be more disciplined about this and stick to also scaffolding the UX .  Once the site is functionally complete I will look for a pre-built template that I can migrate it to.  

I need to eat some humble pie here. For most of my career I have looked at UX designers and thought 
"that's all you do? I could do that if I could be bothered but I want to write code instead".  It turns out that no - I can't do that and I don't have the bandwidth to learn.  

Today's personal lesson is to stick to your strengths and download/buy/hire for the skills you don't.  

Sunday, 10 November 2024

Project 2 - Simplest SAAS - Service Scaffolding

 In this post I'll discuss how I intend to scaffold the article site SAAS so that I can build out the key features without having to worry about integration with back-end services.

The basic idea here will be to take a layered approach, and use an implementation of the Service Layer pattern to encapsulate all of the core business functions of the site.  There will be services responsible for each of the key features of the site - one for user management, one for article management etc.  Services will act as mediators between the Vue components and and back-end logic. The Vue components in the site will only interact with these services and will  never interact directly with a back-end provider.

In addition I will use the Dependency Injection pattern to construct these services.  Where one of these logical service has a dependency on a high layer,  a typescript Interface will be used to define the operations of that layer, and the service will be constructed with a class that implements that interface. 

The Dependency Injection pattern makes it possible to swap different implementations of the service dependencies without requiring the service to be modified.  The Vue components are isolated from any changes to how the back-end dependencies are written,

This in-turn will enable me to create in-memory, scaffolded versions of these key dependencies (user auth, persistence, payment etc)  to get the site running, then enable me to swap to actual implementations with minimal work.  This has the nice advantage of enabling back-end services to be swapped out without affecting the user interface. For example, with this approach I can potentially switch between database host providers minimal impact on the UI. It also simplifies testing - the services can be tested with mocked or faked versions of dependencies if required.

For working with persistent data,  the Repository pattern is often used with Dependency Injection.  A Repository is a specialised service dependency that abstracts all persistence (database) operations behind a well-defined interface - which is exactly what we want!

Here is an example of how this will work for the design requirement of providing the user with a list of the articles that they own.    

  1. The List Articles page (a Vue component) will invoke the get_user_articles() method that is defined on the Article Service.
  2. The Article service will be constructed with an instance of a class that implements the IArticleRepository interface. This interface also defines a get_user_articles() method 
  3. There can be multiple classes that implement this interface.  Any of them can be chosen at design time to construct the service
  4. Initially, I will use an Repository implementation that uses a simple in-memory store to persist user articles (this will reset every time the site is started).  Eventually I will replace this with a Repository implementation that uses a hosted database.
  5. The List Articles page doesn't care which implementation of IArticleRepository is used. It only interacts with the Article Service, and the behaviour of this service never changes. 
  6. This means that the List Articles page can be safely built to work with a simple in-memory implementation of the repository, and no changes to this component will be required when the Article Service is swapped to using a repository implementation that works with a  hosted database



One other thing I should also mention is that the data types that are sent to and from the the dependent layers should also be defined. This enables a type safe definition of each interface.  For the example above, as well as defining the IArticleRepository interface, we should also define an Article type for the repository to return.  These data types can be thought of as the Models in our design.

Using this approach takes some initial set up but I believe that it will pay significant dividends in enabling me to set up a fully featured user interface without having to worry about the complexities of integrating back-end services. I can focus on implementing the core features of the site first and save significant time on my first few iterations.

Thursday, 31 October 2024

Project 2 - Simplest SAAS - Concept and Approach

In my quest to build a side-hustle software-as-a-service business, I am building and deploying projects to level up my front-end and full stack development skills.  Now that I have completed and deployed my first Vue based solution, it is time to move on to something more challenging.

For my second project, I want to build and deploy a complete but minimalist software-as-a-service site.  To meet my self-imposed definition-of-done, this site will need to be functionally complete, hosted and have some potential to actually make money.  The site will also need to have a responsive UI that works on desktop, tablet and mobile platforms.

Since this will be a SAAS solution, it has to have something to sell. For this site the concept will be the simplest thing I can think of. The site will enable members to create and publish permanent 'bulletin board' articles. Users will pay a nominal amount for each article that they want to publish to the world.

The 'elevator pitch' here is that it is actually not easy to publish a simple 'open letter' to the world with any degree of permanence or which isn't either behind a paywall or saturated with adverts. Even a blog site like this one requires some effort.  A simpler 'no frills' option may be more attractive.  

Anyway, even if this concept isn't a winner, it doesn't really matter. The purpose of this exercise is to 'learn by doing' rather than making an actual profit, but I want at least to have some kind of product so that I can look at building the sort of account/payment features that a SAAS site demands.  Keeping the core site concept simple will enable be to focus on learning how a SAAS ecosystem works.
 
Unlike my first project which was front-end only, this solution will require multiple layers. Eventually I will need to integrate some sort of persistence layer, an authentication service,  and of course a payment provider. 

However, because another key focus of this project will be improving my woeful UX skills,  I am going to take a UI-first approach, where I will abstract and simulate the back end layers to start with. The general idea will be to get a fully featured version of the UI working first, then look at integrating back-end services.  I'll also learn from my previous project mistakes and start building a test suite from day one!

Here is the basic list of features that the solution will need to support

  • Customers - Core Features
    • Create, Edit and Delete articles
    • Publish/unpublish articles
  • Customers- Account/Billing Features
    • View/Modify Account Details
    • Purchase article publishing 'slots'
    • View Order History 
  • Authentication
    • Sign up new users
    • Login and authorise customers
  • Support
    • Support/feedback page
  • Public Users
    • View Published Articles

I think this covers the minimal set of features a SAAS solution will need.  If I get these working then I should be able to use this as a reference for building more complex SAAS  solutions for future projects.

The technology stack for the front-end will be identical to that of the first project:  Vue, Vuetify, Vite and Vitest all seem to work well together and this will be a chance to do a deeper dive with them,

I'll leave a deep discussion of the technology stack for the back-end for a future post, but eventually I aim to integrate the front-end with Supabase for Authentication and Persistence and with Lemon Squeezy as a payment provider. 


Saturday, 19 October 2024

Project1 - HowMuchIsThisMeetingCosting - Mistakes were made!

I am trying to level up my front-end development skills as part of a plan to build a side-hustle business. In the previous posts I have been discussing my first project  - howmuchisthismeetingcosting.com - a simple front-end only site for calculating the live cost of a meeting.

I was excited to finish and deploy this first project. Getting a side project - even a simple one - over the finish line is no small  feat.  In software development the Pareto Principle is often considered to apply: finishing the last 20% of a solution can take 80% of your effort.  For a side project or a self-training project it is all too easy to avoid this overhead and quit when the project is 'good enough', but not done.

I am completely guilty of this. I am a serial project starter and rarely get something finished before I get distracted by something shiny. One of the drives behind picking a simple project was to pick a task that wouldn't be too challenging to finish, and to start trying to get out of the bad habit of abandoning projects before they are done.

This is one of the core ideas from the book 'Atomic Habits' by James Clear.  To build positive habits or get rid of bad ones,  start by making easy to achieve changes and do them enough that it becomes automatic. Set the cadence with easy to achieve actions and then add complexity/challenge _after_ the desired behaviour has become automatic and habitual.  In my case I want to start building a habit of actually finishing projects - and one way to do this is to make sure that I start with projects that aren't overly challenging to get done.

Given all that, I was pleased to get my first project over the line.I was so excited that I couldn't wait to show it off.  Look friends - I actually finished something!  Cheers and Congratulations to me! 

Then, about 30 seconds after trying to show the site off, a good friend of mine pointed out that the meeting cost numbers were not making any sense. The site was miscalculating the cost by a factor of around 100. The web site - which was intended to be a small blow for the cubicle worker against management bureaucracy and waste - was making it seem like pointless meetings were 100 times cheaper than they should be!

In my excitement to get something finished, I used the 'hold my beer' approach to software review and testing . The site was simple enough - and it seemed to be working - surely eyeballing it would be fine, right? Wrong.

At the heart of this website is a simple typescript class that is responsible for maintaining the running state of the meeting timer (src/components/utilities/timerstate.ts in the source).  This class is a stateful service - a javascript singleton - that keeps track of the number of seconds that have elapsed in the meeting and use this to construct a string that displays the elapsed meeting cost, this string is then stored in a reactive variable that updates the HTML display.  

In the original version, that was all done in a single method. The method was about 20 lines long and looked simple enough.  What could go wrong?    Well obviously something did. 

To construct the meeting cost,  I calculate the total cost of the meeting in cents,  use a floor division by 100 to find the dollar component, then use the remainder to construct the cents string.

let cost_per_second = (this.avg_cost_per_hour * this.no_participants) / 3600;
let total_cost = cost_per_second * this.number_of_seconds;
let dollars = Math.floor(total_cost / 100);
let cents = Math.floor(total_cost - dollars * 100);
let dollarString = dollars.toString();
let centsString = cents.toString().padStart(2, "0");
this.cost_display_string.value = this.currency_symbol + dollarString + "." + centsString;


This issue here of cause is that I am actually calculating the total_cost as dollars not cents which is why the cost is out by a factor of 100.

The fix here is simple, but the fact that it occurred is not great. There are several contributing factors:
  • there are no unit tests. There wasn't even a test framework installed in the project
  • even if there were unit tests, this code isn't written to be well tested. It is making unpure calculations that change the state of the class, making them hard to isolate  
  • the variables are poorly named. if I expect the total cost to be in cents, it should be named something like 'total_cost_cents'.
In my rush to develop a good habit (getting something finished) I didn't look at reviewing this code, or taking the time to perform even basic unit testing.  For a simple site like this the harm was just my embarrassment, but if I want to eventually have my own paying customers then I need to do much better.

(there is a tension between learning new things and building robust things to navigate here. I think it is OK to make mistakes when learning, but the failure here was not being disciplined enough reviewing the solution before calling it 'done' and shipping it. This is probably much like writing a book. I have read that is OK to make mistakes in your first draft and being overly picky at first can probably break your flow, but that you need to be rigorous about editing once the draft is done.)

Anyway, deploying a broken site is unacceptable and the must rot stop here! Although this was just a 1 line fix, I decided to do things better.  I refactored that class for testability by moving the calculations into pure functions, and for readability by improving the variable names.  I then installed vitest and wrote some simple unit tests to make sure that the problem was fixed.  The relevant part of the class look much better now, and more importantly they actually work.

calculate_cost_per_second(cost_per_hour: number, no_particpants: number) {
    return (cost_per_hour * no_particpants) / 3600.0;
  }

  calculate_current_cost_cents(
    cost_per_second: number,
    no_seconds_elapsed: number
  ) {
    return cost_per_second * no_seconds_elapsed * 100;
  }

  calculate_cost_display_string(
    total_cost_cents: number,
    currency_symbol: string
  ) {
    let dollars = Math.floor(total_cost_cents / 100);
    let cents = Math.floor(total_cost_cents - dollars * 100);
    let dollarString = dollars.toString();
    let centsString = cents.toString().padStart(2, "0");

    return currency_symbol + dollarString + "." + centsString;
  }


vitest seems to be another of the v-family of products that are built around Vue (vue, vuetify, vite,)  that just works - it integrates nicely with vite and is simple to learn quickly.  There really wasn't much excuse not to use it. With the calculations extracted into pure functions they become easy to test:


test("MeetingTimer - calculate_cost_per_second", () => {
  const timer = new MeetingTimer();

  const no_people = 100;
  const avg_cost_per_hour = 100;
  const num_seconds_per_hour = 60 * 60;
  const expected = (no_people * avg_cost_per_hour) / num_seconds_per_hour; //100 people at 100 avf cost per hour
  const actual = timer.calculate_cost_per_second(no_people, avg_cost_per_hour);

  expect(actual).toBe(expected);
});

Now that I know the site is fixed, I have redeployed it and it is back to doing its job - waiting for the chance to passive-aggressively shame time wasters. 
 
I think the key take away lesson for this is that I need to be more disciplined about reviewing and testing code, and that not to let the sugar rush of getting something 'done'  overwhelm the need to do things 'right'.

Moving Blog Platforms

 After struggling for weeks with Google Search Console and Blogger settings, I've come to the conclusion that I need to move to a differ...