A year of helping tradies get online: My 2021

I started working at Kwotimation about October 2020, and as I write this article it's currently February 2022. I'd wanted to do a one year retrospective in addition to my current writeup of my first few months, but I never managed to find the time to do so between all of my other responsibilities—it wasn't really a priority for me because I figured that anyone could just look at the software and get a sense for what's happened in the time since then. Today that changes, and I'm going to document the highlights of what's occurred.

It was an incredibly busy year, and we covered a huge amount of ground given the resources available to me. This was an early-stage startup with no customers and only NZ$500K in investment. The high level overview is as follows:

Covering everything we accomplished in 2021 would make for a remarkably long post, so I will only concentrate on the four major feature releases we made that year.

Features

Responsive design (January)

Kwotimation's MVP had only been designed for computer users, as we'd made the assumption that most people wouldn't be managing their business from a phone. This assumption held true up until it was time to start running online ads, because of course most people viewing those ads would be doing so from a phone: most social media users are on mobiles, and desktop users have significantly higher rates of ad blocking extensions. Very few companies—and no brand new companies—are in a position where they can have a phone user click on an ad, and then rely on that user to come back at a later date to check out the software once they're back home and in front of a computer.

The desktop pricing settings page

Pages like the pricing settings page pictured above are challenging to translate to a mobile phone's small screen because of how much data is displayed on screen and the number of different interaction points. The tiling industry has support for 17 different services across 4 categories, and each of these services has metadata attached such as an average unit rate and the list of materials the tiler uses on jobs. A 1:1 translation of the desktop interface with everything stacked vertically does not work here due to lack of space—especially when considering small phones like the iPhone SE or phones with odd aspect ratios like the Galaxy Flip series.

Mobile interface of the pricing page; collapsedMobile interface of the pricing page; expanded

In this case, we were able to come up with a nice interface which uses a combination of accordions to hide the list of materials on first page load, and reduced padding to maximize the amount of usable screen real estate. For other pages, we had to use different strategies: the list of quotes the tiler was displayed in data grid style on desktop so we swapped that out for a list on mobile, and the website designer's interface was pretty radically changed in order to accommodate the smaller screen of a phone.

Interactive quote (Feb—April)

While teaching NZOI's 2020 January bootcamp at the Xero offices in Parnell, I got to thinking about the scope of the Kwotimation product. We had an online pricing system which produced PDF quotes and emailed them to customers and tilers, and on the roadmep we intended to add an online store for selling tiles. The workflow seemed limited: after getting a PDF, there was nothing more to do and because our quotes were immutable it meant that to make a variation users would need to make an entirely new quote using the pricing system which incorporated that variation. It also wasn't possible to add custom, one-off services to quotes when a customer's project didn't fit neatly into our pricing model which limited its usefulness.

It was clear to me that we needed to rethink the way we sent out quotes to set us up better for tile purchasing and to better meet user expectations of the system. My proposal was to throw away the PDF quote in favor of a web-based quote which both the tiler and customer could interact with. This web-based quote would, in future, be the perfect location for recording information about tile orders as well.

The proposal was accepted by the founders, and then we began scoping out the feature. We had an approximately 2-hour long discovery call afterwards, so that I could immerse myself in and really understand the quoting process from start to finish. I needed to know what steps are involved after a quote is initially presented to a customer so that I could find the best technological solution. I've included a snippet of the extensive notes I took following this call below.

Excerpt of notes taken during discovery call of the interactive kwote

'Kwotes' (as we call them at Kwotimation) are large JSON documents stored within MongoDB. The documents are denormalized, which proved an advantage for us here because we want things like service prices to be specific to an individual kwote—in other words, we don't want to join against a 'services' table and fetch the most up-to-date price the tiler has configured, because doing so would change all past kwotes which customers have seen and potentially agreed to. While parts of Kwotimation's data is highly relational and would benefit from being stored in an RDBMS, the kwote documents leverage MongoDB's feature set extremely well.

The biggest technical hurdle to get over was around variation tracking. Other software in the space of online quotes take the approach of saving each version of the quote as a separate document and provide the user with the ability to flick between those versions in order to scan for differences (this is the case with Fergus) or simply show the most up-to-date quote at all times and don't really have much UI for viewing the actual changes (like Quotient).

One of Kwotimation's core values is transparency: instead of customers needing to assume tradies are accurately pricing variations, for instance, the tradie can instead just put the altered service into the pricing system and get a reliable price instantly. In some sense, the online pricing system is to the trades as Uber is to transport; instead of having no idea how much your taxi will cost until you arrive at your destination, you now have a way of knowing before you make a commitment. Due to this, we were very committed to coming up with an interface which would highlight changes made to the quote so that customers were always informed about what was happening. We also wanted to build a suggestions system which would allow customers to request specific changes on-platform, instead of needing to shoot off an email. One of the problem areas we were trying to solve with this online quote—in addition to everything else—was freeing up tradies' communication channels so that everything is in one place instead of scattered across a collection of emails and text messages.

I made the decision that we'd track changes made to kwotes by using the JSON patch standard. After patches were applied, a function would be run which identified the changes which occurred and then those changes would be reported to the user. We extended the JSON patch standard so that patches also had an optional isSuggested field, which when true would indicate that the customer had requested the change represented by the patch.

Development of this feature dragged. There were so many little details to get correct, and the number of interaction points was huge. While we were in the process of developing this feature we also had to maintain the current system, and with the small team this fragmentation of our attention was very costly to the project timeline. Originally there were plans to have a UX designer come in and design the interface and workflow, but that task had fallen to me after the company had received quotes for the work and decided that it would be too expensive to go down that route.

Tracking changes by inspecting JSON patches seemed easy enough to do at first, but it turned out to be a real nightmare once things got a bit more complicated. Because patches work on indices it became very difficult to keep track of the intermediate document once items inside arrays started to get deleted, and the problem was magnified in difficulty because the kwote object actually had a 2D array: there's an array of areas, and then within each area there's an array of services and users are able to make changes to both of these things.

After a few weeks I had something which worked reasonably well, and we were able to deal with most bugs which cropped up over time. This all came screeching to a halt later on in the year, when in September we encountered a bug which didn't seem possible to solve within the constraints of the system I'd built. It occurred when a customer would add a service (as if to suggest it), and then delete that suggested service from the kwote before then going on to save their changes. The diff tracing module and the module which handled suggestions interacted with each other in such a way that this caused the whole kwote to break. We were able to work around this issue by catching the specific exception raised in this case and rendering nothing in the React component, but it wasn't a great fix and it was unclear whether there were other possible landmines waiting to be discovered.

Fortunately this happened during a time when we were inbetween major features and were busy fixing bugs and making incremental improvements to the software. I was able to spend a week reworking the interactive kwote so that instead of using JSON patch we used a custom patch system which didn't require us to interpret the generic patch types of JSON patch and deal with interpreting indices; instead we were able to simply apply a "delete area" patch to some area's ID. Since making this change, we haven't had any issues at all raised by customers or tilers about the kwote breaking.

Since releasing the interactive kwote to our end users, we've seen fantastic engagement. Both our subscribers and their customers really like using this feature, and consider it far superior to the "traditional" quoting method which involves shooting off PDFs over email. This concept I formed during the NZOI bootcamp has turned out to be one of our stickiest features, and represents a real competitive advantage for Kwotimation as there really isn't a system out there which does exactly what our kwote does.

The most surprising aspect of the kwote's success was how popular the chat feature is: every kwote has a real-time chat attached to the side which allows tilers and customers to communicate with each other directly from the kwote, and this is by far the kwote's most popular feature. The main reason why people like it is precisely because it frees up their email inbox and text messages, and allows all of the communication related to the job to be placed in one central location. When we were scoping out the interactive kwote, chat was added on mostly as a "why not?" feature, and I'm glad we did because we did not expect people to get as much use out of it as they have so far.

Partners portal (April)

To sell tiles, we needed to partner with a tile store and we were in talks with a few different businesses. During these talks, we discovered that there was another value add we could bring to the table: building software for managing a registry of local tilers for each store. Customers looking for tilers will often go to tile stores to enquire about tilers, and this consumes store resources and also potentially opens the store up to liability if a tiler they mention ends up doing a poor job. Digitizing this aspect of the stores solves both problems, as it's much easier to throw up a disclaimer on a digital system compared to training all staff members at the store.

Screenshot of the partners portal landing pageScreenshot of the partners portal tilers list

Over the course of about a week, I had the team whip up an initial implementation of what we came to call the "partners portal." The backend still had a bit of work to do, but I made sure that the frontend was slick—because as Jeff Atwood once said, "the user interface is the application"1 and people put a lot of stock into the visual appearance of software. All of the tile stores we showed the portal off to were incredibly impressed, and we were able to secure a mutually beneficial deal with Tile Depot. The portal was deployed to the world around the end of the month, and QR codes were put up in stores which customers were able to scan in order to access that store's particular portal. One of the changes COVID19 has caused in society is that now everyone—even my grandmother—is able to confidently scan QR codes, which made this system work really well.

We've found that on average, stores with QR codes deployed will list 11 local tilers. Three quarters of these tilers are not Kwotimation customers, but those who are signed up with Kwotimation see a small but noticeable increase in their number of website visitors per week.

We moved very quickly on this. Prior to the meeting with Tile Depot our COO had seen the deployed staging version of the portal but our CEO hadn't, and when the COO loaded up the portal our CEO initially thought we were just showing mockups of how the system might look in the future.

Online store (April—August)

In parallel to the partners portal, we started in earnest on implementing the tile store. While we did not have a store partner yet, we created some dummy data and started work on components we knew we needed like cards for displaying products, and the products filter list. The design for this part of the software was based on mockups we'd made at the end of 2020 when we first started development of the MVP. You can see the final interface below.

The final product browsing UI

When working on projects, part of my job is to identify risks early and manage them appropriately. The major risk with this new piece of functionality was in the integration with the tile store we partnered with. David Hallett of Company-X once said to me that "an API isn't an API until you've got it", and he's absolutely correct: API quality varies drastically, and a poorly-documented or designed API can explode project timelines.

There are endless articles written online about choosing the correct tech stack for the problem you're solving, and when we were seeking preseed investment for Kwotimation almost every investor enquired about our stack. There are fewer articles about picking good API partners, and I think software companys' lack of attention on this facet of their business is a mistake. The API of the company you're dealing with becomes a part of your tech stack the moment you start integration efforts and it is critical to understand what that means for you long-term.

As it turned out we didn't get an API. Instead, our tile partner was to upload a .csv file to an FTP server we owned every hour, and we would parse out that data into something our software could understand. When it came time to making an order, we would programmatically send an email to one of their inboxes with the list of necessary items and the delivery address. Clearly, the API in this case was sub optimal.

It took a couple of weeks to get everything set up—we didn't even have an FTP server at the point in time where the agreement was finalized, because we simply didn't require one. None of our other API providers handle their data interchange via FTP. Getting a server up in a vacuum is straightforward, but due to our small team I placed high value on ensuring our services and infrastructure was internally consistent so as to reduce the amount of "one-off" knowledge staff needed to acquire.

The solution was to sync the FTP user's home directory to an S3 bucket using a program called s3fs. Having the data in S3 allowed us to do the following:

  1. Backups could be managed as part of our pre-existing S3 policies. This meant we could treat the FTP server's contents as ephemeral, as the most up-to-date version of the data was always on S3 and historical data could be accessed through our backups.
  2. We could write a serverless AWS Lambda function which would be triggered whenever a new data export was saved to S3. There was no need to, for example, run a cron job which pulled data from the FTP server. We could simply add another function to our suite of Serverless framework projects.

The overall architecture looks as below:

Diagram showing how data moves from the FTP server to our backend API

For actually storing the data, I looked to the Shopify docs for inspiration on how to structure our data model. We made some slight modifications to better suit our use case (we added a structured "specifications" field which stores tile-specific information such as its antislip rating, for instance) and wound up with a system which works very well.

Having completed the task of tracking inventory and displaying a shopfront to end users, we weren't entirely finished yet. The online store integrates with Kwotimation's online pricing system, allowing end users to purchase tiles as they get a quote for their project's labor costs. This means the selected products needed to be displayed on the interactive kwote, and the purchase needed to happen once the kwote was accepted and finalized.

Adding new components to display the order information was straightforward, but we needed to rework how price data was shown on the interactive kwote. Prices shown to the customer on the storefront are inclusive of GST (as you would expect from a consumer-focused interface), but we need to show tilers the GST-exclusive price to ease their accounting process.

This relatively simple sounding task was made much more complicated by the fact that tilers are able to assign complicated discounts to their kwote, and that we also need to account for any applicable delivery fees. A lot of bugs came up while we were working on this, and it took a fair bit longer than originally anticipated.

To not confuse our subscribers, we decided that the customer would pay the tiler off platform for the products at the same time they paid the tiler their labor down payment. The tiler would then access an online checkout interface from the interactive kwote where they would purchase the tiles so they could be delivered to the customer.

Checkout screen, which shows the tiler how much they're earning

Again, we looked at the Shopify checkout interface for inspiration. From the kwote, we were able to prefill all of the delivery information and we were able to simplify the payment step by simply reusing the subscriber's payment method on file that they use for their subscription. We also needed to add an "earnings" text below the list of products being purchased so that tilers could easily see their take-home payment.

I really like the tile store feature. Individual tilers have relatively little power in the market they work within because all of the tile stores are large companies with all of the leverage. At Kwotimation, we were able to both reduce tilers' dependence on the stores for acquiring customers through providing them with a website, and were also able to put more money into tilers' pockets by negotiating a revenue share model on their behalf.

Tilers around New Zealand have used this feature to add thousands of dollars to their business' bottom line, without needing to do any extra work themselves. That kind of positive outcome is what I originally got into software engineering to achieve, and it was particularly fantastic to do for a demographic which so many people take for granted. In future, Kwotimation wants to offer this feature to other countries and industries by negotiating similar win-win deals with other suppliers.

Website builder (October—2022)

Our website builder up until now was not one of our core offerings. We made it available for those who wanted it, but we were much more concerned with offering a fantastic online pricing experience. In October we started to shift gears and reprioritize the website builder in response to market sentiment, and from a desire to expand our addressable market. Building online pricing systems took time, but there were over a million tradies spread across different industries in Australia and New Zealand we could potentially sell websites to today.

Our website builder allowed subscribers to customize the text and images on a landing page with a predetermined layout. You can see the original mockup from early 2020 by clicking this link, and a mockup of the MVP's version by clicking this link. Note that I did not design these mockups.

To make this a product people wanted to purchase, we needed to add more features to meet customer expectations around website builders:

Whenever I create a website for a client, I break down the design into horizontal "strips" of content. The markup for the home page of this website, for instance, is just a series of Section components with different props applied. I knew we could do something similar with the website builder.

I decided that we would design and build a collection of "strips" which users would be able to add, edit, and rearrange on their web pages. Different strips would have different customization options available, and would have a unique visual appearance. One strip, for instance, might display a gallery of images while another one displays a collection of cards with marketing copy.

Working with my COO, we came up with a collection of initial strip designs and I assigned them to our developers. This meant we had a bunch of grunt work to push out, giving me time to consider the best way of structuring the rest of the code changes. MongoDB's schemaless design came in handy here because it meant I could store each strip's configuration options as one big JSON object and then pass that JSON object directly to the strip's component as props, which made that aspect straightforward.

We kept the idea of editing text inline from the page, but when you do click on a strip we reveal a floating toolbar. The toolbar has options for deleting and moving strips, and also has a "Customize" button which opens up a draggable pane. Within this pane are all of the configuration options for that particular strip. You can see an example of this in action below.

Blue adder buttons appear at the top and bottom of the active strip as well, which can be clicked to reveal the "add strip" interface. All of our strips are developed in a package hooked up to a storybook, and we have a set of custom scripts built on top of Babel which parses out the default props for each strip, takes screenshots (using a modified version of storycap), and writes out a manifest file with all available strips the user can add. This means any of the preconfigured strips we put into storybook become available from the website builder UI. I'm quite proud of this approach and haven't seen it in use elsewhere.

Responsive design was again a challenge here. Most website builders currently available do not support use from a mobile phone, because there are simply too many different interaction points to comfortably fit within the constraints of the mobile form factor. It is an incredible UI/UX challenge to solve properly once, and once more features are added it needs to be solved all over again.

I decided that on mobile a card would slide out from the bottom of the screen which contained the strip's configuration options, rather than use a floating draggable pane. This is quite a natural interface if you're familiar with apps such as Google Maps or Discord which both use a similar interface pattern. Challenges we encountered centered around how to direct the user's focus to the important parts of the page: careful management of the viewport and color were employed in order to minimize the time spent by users scanning the interface.

Once the other parts of infrastructure were in place, rendering the strips was straightforward. All we need to do is map over the list of strips the user has set up, look up the component for that strip type, and render it with the user's options supplied as props. User sentiment for this feature is overwhelmingly positive: the additional degree of control afforded to them has allowed them to more accurately and comprehensively express their business' personality and expertise.

Conclusion

2021 was an incredibly busy year for Kwotimation and myself. In addition to building out the features described above, we also started selling the product to tilers over in Australia and rolled out a version of the online pricing system for painters. Over the course of the year I had to solve a wide range of problems, manage people on my team, design and scope work, and conduct user research to improve our user experience.

We've started 2022 off strong by further building on top of the website builder, and are looking to form key industry partnerships which will cement the long-term competitive advantage of the business.



  1. As an aside on this, one of the very few tech demo failures we ever had was just prior to closing out the preseed round when I was demoing the product to an investor. I'd only just recently made the domain name field of the organizations collection have a unique index, and MongoDB's unique indexes consider null as a value by default (unlike in, say, PostgreSQL). Newly created organizations had their domain set to null, which meant that registering a second organization without first setting up that initial organization's domain name would break the sign up page. This is exactly what ended up happening during the demo! The investor said something along the lines of "clearly there's a lot of work still to be done." About five minutes after the call had ended I'd deployed a version which used a sparse unique index; fixing the problem. The software was in a really good state, but small UI issues like that have a very disproportionate effect on how other people perceive the overall system. I think this is one of the biggest lessons every software engineer learns during their career: very few people actually care about the underlying technology.

Get in touch 👋

If you're working on an innovative web or AI software product, then I'd love to hear about it. If we both see value in working together, we can move forward. And if not—we both had a nice chat and have a new connection.
Send me an email at hello@sophiabits.com