Unconventional Startup Architecture — For The Win!

Clifford Oravec
Entrepreneurship Handbook
19 min readNov 16, 2017

--

If you didn’t know, we do things a bit differently here in Startup Land, including the way we sling code. Here’s where you’ve probably been going wrong (and what to do about it).

Fun fact: The shiny lights help me crush it that much harder.

A word of warning: What follows may not be for you.

If you find completeness, clarity, and a sense of purpose in designing elegant applications featuring domain-driven design, layers of abstraction, IoC containers, dependency injection, RESTful APIs, mock objects, and automated build tools with CI and comprehensive automated test coverage, you may want to just turn around and slowly close this door behind you.

Go ahead, it’s okay. I understand.

But if you want to learn why you’ve never been able to ship any of the “startup” projects you’ve worked on tirelessly for months or even years at a time (and which are still sitting sad and alone in some random Git repo you created for them, never seeing the beautiful light of day), then come on in and grab a chair.

Because I’m going to share some of the unconventional startup architecture tips and tricks I’ve learned building (and shipping!) bootstrapped startups all by my lonesome, including the things I’ve learned building and growing Tamboo — a session recording and heat map tool for websites — my most technically ambitious business venture to date.

I Used To Be A Solutions Architect. Then I Started A Software Business.

I used to be that random stock photo startup hipster whiteboarding away just like you!

Ahhh. Memories.

The smell of dry erase markers on a whiteboard as I stand in front of a crowd of my colleagues and teammates, piecing together the techncial puzzles that have eluded them for years.

The feel of the Starbucks coffee cup in my hand as I take caffeine-laced swigs in between fielding questions from those gathered here.

The beautiful projection of my Visio masterpieces illuminating the wall beside me for all to see.

The smug feeling of knowing that I’ve found every conceivable fault, exception, bottleneck, timing issue, race condition, gotcha, hiccup — even gremlin — in the system that we’re now discussing — and that I’ve planned for all of them in my design.

And not just that — but my design is beautiful. Dead sexy even. And I know it.

Elegant yet unintuitive. Deceivingly simple yet tough as nails. The kind of thing that makes perfect sense when you see it — but something you never would have thought to have put together yourself given the pieces in front of you.

I was at the top of my game, and I loved everything about it.

I was a certified whiteboard connoisseur — I obsessed over the tactile feel and visual aesthetic of the brands and styles of the available dry erase markers on the market. I knew which color and tip size combinations worked most effectively together to best capture my game-changing insights and ideas — while still dazzling the people seated far across the room.

I had built out a (very expensive) private library of the very best technology and architecture books on the market — and had read every one of them. More than once in most cases. The tomes with the fancy ribbons in them were my personal favorites to showcase. The more esoteric (and expensive), the better.

The glorious “ilities” were my philosophy and religion.

I ruthlessly chased down and consumed every shiny red ball that crossed my voracious path (including some of those devilishly slippery tricksters — Scala, Haskell, Eiffel, Clojure, and friends).

All in all, I was what you might call “a developer’s developer”.

So in my mind, it made “perfect sense” that I should fully leverage my impressive skillset and start a software-based business. After all, I should be able to make way more running my own company than working for someone else, right?

This Was Supposed To Be Easy.

It’s almost done! I swear it! I just need to refactor the database *one more time* to better handle scale under load and then I need to finish rewriting the frontend to use Vue instead of React— and then I think it’ll be ready to do a closed beta with that one guy that signed up for my launch list two years ago!

Things didn’t go exactly as planned, to say the least.

And they didn’t go as quickly as I expected, either.

(But what’s a lost decade give or take a few years between good friends, right?)

Although I learned a lot about building startups during that time, one of the biggest traps that I kept falling into (and one that I see fellow developers languishing in all the time) is treating a startup’s codebase and architecture like it’s a doctoral thesis.

Meaning, there is this tendency and this desire to make your startup’s technology awesome and ridiculously impressive, using only the latest and greatest techniques, approaches, tools, languages, and frameworks.

For some of us, our startup somehow becomes a reflection of ourselves.

It becomes in our mind the realization of all of our acquired knowledge and skills.

It becomes the culmination of our life’s work.

We want it to be perfect — to be our pièce de résistance.

Because for most of us, that’s why we wanted to build a startup in the first place — to build, work on, and live off of something that embodies the ellusive technical purity we’ve chased the majority of our careers but never found (but that we know must exist!)

And because we somehow entangle our own identities with the bits and bytes and function calls of our codebases — we are almost always dissatisfied with the end result — and do everything in our power to avoid “shipping it” into the world, where all can see our faults.

Of course, this is in stark contrast to our ability to ship things at our jobs.

If we didn’t have the working history to believe that we could code and ship something (often in just a weekend!) we probably would never have entertained this idea in the first place.

So why is that?

Why can we ship it like it’s nobody’s business at work, but when it comes time to ship our own “life’s work” as it were — the thing that should buy us freedom and make us proud of ourselves— we freeze up and find every reason to tinker, prod, poke (and even shelve) our projects instead of shipping them and going live?

Stop Being So Stupidly Serious. (Or Seriously Stupid.)

You’re not a business yet. So stop trying to act like one. And even then, just don’t.

Before we delve into the technical details about how you should probably being going about architecting and designing your startup, we first need to talk about your attitude.

You see, us developers tend to be pretty serious about, well, development.

Not to mention opinionated.

(You don’t say, right?)

But here’s the thing you need to realize — all of those opinions, preferences, and “best practices” you’ve committed to memory and tattooed on your soul like a set of holy mantras handed down from on high (Don’t Repeat Yourself! Single Points of Authority! Separation of Concerns! SOLID!) were likely developed and employed (religiously at that) in a professional setting.

And the first thing that you’ll find in building a startup is that professionalism is for amateurs.

When you write code professionally for an employer for a paycheck, you follow one set of rules (the ones you already know and love).

When you write code for a speculative venture on your own time with no immediate path to revenue, you need to follow a completely different set of rules (or you’ll never cross the finish line).

That’s not to say that you shouldn’t do those things at your day job — you very well should! Because that’s what your employer is paying you for. To write clean, maintainable code. To write documentation. To create unit and integration tests. To use version control and build automation. Because they’re paying for you to contribute to a system that must work reliably for their users and must be able to run after you quit or get fired.

But here’s the thing.

Your employer has stable revenue, an established position in the market, and most of all — actual users using their applications— and you don’t.

All of these “concerns” that us software developers obsess over only matter when you have code that is actually used by end users and that is worth the time and investment of making it more stable, more reliable, more maintainable, and more fault tolerant.

No users? No reason to do those things.

Those are strategies used to help manage an application with a growing codebase, a growing footprint, a growing user base, a growing everything. Those are not strategies that will help get you off the ground. Because they’re not meant to.

It’s asinine to be concerned about growth when you have zero traction.

That’s like spending years trying to figure out the gory mundane details about how and where you’ll manage all the millions of dollars you have yet to ever earn (and trying to learn all of the tax implications of those moves as well!)

That’s the first part.

The second part is this odd emotional attachment people have about their “personal projects”.

I get it, you’re proud of the work you do.

And there’s nothing wrong with that.

But what is wrong is when you treat “your” code any differently than you would code that you would write for someone else.

Just because it’s “your” code does not mean that it should be any better than the code you get paid to write for a living.

If the code you write for them is good enough and “just works” — then why can’t you just do the same thing for yourself and stop over engineering everything?

It’s because you view it as “your” code that you expect perfection.

Because hey, you’re going to be making a living off of this thing, right? So it better rock!

And that’s the issue.

When you think of a problem, goal, or situation a certain way, chances are that’s how you’re going to go about tackling it (rather than being open to alternate views and options).

In order to think clearly about your code — and to make sure you do things that make business sense — you need to create some emotional distance.

So how do you do that?

Simple.

You reframe how you look at it.

Rather than thinking that you’re going to be building something “for yourself”, convince yourself that you’re going to be building something “for a client” or “for your boss”.

And your client/boss has a real tight deadline — and an even smaller budget.

But they have to have something for their users — and they expect you to deliver it within those parameters.

Changes the way you look at things, doesn’t it?

But that’s exactly what you have to do.

You have to treat yourself like a cheap-ass client/boss who wants something functional ASAP.

And then you have to go and do it.

I’m Not Talking This.

Your desk might look like this. But your app better not.

Now, let me be clear that we’re not going to be throwing shit against the wall just to see what sticks. (It turns out shit does not in fact stick very well on walls. At all. Just trust me on this.)

What I am talking about is designing and working within constraints.

Constraints force you to focus on what’s most important for the task at hand.

When you can do anything and everything — chances are you’ll probably try to (or be paralyzed by all the options available).

When you’re limited to doing one — and only one thing — then you can devote your entire focus to doing that one thing, and doing it well.

It’s more like taking a zen aesthetics approach to your architecture and design: It’s not just what’s there — it’s also what’s not there that makes it beautiful and elegant.

Using this approach, your goal is to distill all of the crazy ideas and “what-ifs” you have running around in your head down to the simplest possible solution (yet no simpler!) that puts the most emphasis on the most important aspects of your application.

Your focus should be on executing your application’s critical path ridiculously well while balancing the constraints you’ve been tasked with in order to extract the most ROI (Return On Investment) possible from the effort you’ve put in on behalf of your overbearing “client/boss”.

Let’s Break Some Bogus Misconceptions.

Yes way, dude. Yes way.

Okay, so if you’re still with me, it’s time we let the dogs out and got this party started.

I’m going to start by reminding you what architecture and design really are, because it’s critical we be on the same page for the sake of this discussion:

Architecture is concerned with the creation, identification, classification, description, and documentation of the structures which constitute a system — be it software, hardware, or otherwise.

Design is concerned with the development of a specification for a solution which achieves some specific goal while adhering to certain criteria and constraints.

Yep, that’s it kids.

It really is that simple.

Now, the thing we need to pay attention to here are some keywords from each of these definitions — namely “structures”, “goals”, and “criteria and constraints”.

Because these are the parts that you’ve been screwing up.

The sad sorry truth is that most startups are just glorified CRUD (Create, Read, Update, Delete) apps — much like you probably build at your day job. Sure, we sprinkle a healthy pinch of unicorn droppings on them for good effect, but it doesn’t change the fact that it’s just CRUD under the hood.

The structures that constitute and support a CRUD application are well-known to just about anyone working as a professional software developer nowadays. At least until they try to build a startup. Then they just get cray with whatever hot new technology or technique they can get their hands on.

So the first thing you need to admit to yourself is that you’re not doing anything earth-shattering with your codebase. It’s a CRUD app in some way. Treat it as such. Stop trying to be fancy about it.

If you know how to MVC and ORM in some language and framework, just do it “the standard way” you’d do it at work and be done with it.

Now is not the time to play with new technology.

I know everyone thinks “Oh, I’ll build my new startup in this new framework because it’s so damned cool and at least I’ll get to learn that framework if my startup doesn’t work out.”

Yeah, and that’s exactly why your startup will not work out.

Because you put technology before business, when it must be the other way around.

You should know the technology stack you use to build your startup. In and out. You should use it every day. It should come as — and have no — surprises for you. It should be as boring and as familiar as that ragged college t-shirt you wear to bed every night.

Wear it. Own it. And move on.

Because if you don’t understand a language or a framework fully, there is no way you can design or architect for it efficiently or effectively. You put your startup’s entire future at risk when you violate this principle.

And now is not the time to learn a new language or framework. Do that on your boss’s time — not on your own.

Okay, so where was I?

Oh, yeah.

You want to pick whatever technology stack you already know.

Clifford’s Rule states: Only build your startup using a technology that you can code in without having to Google for answers or copy and paste from StackOverflow to make work.

Yes, relational databases are boring. Use them anyways.

Yes, MVC is so 2008. Suck it up and use it.

Of course REST APIs are hot. Why do you need one?

SPAs are sexy, sure. But do you have experience building one?

React/Vue/Meteor are all everyone is talking about. Let them talk.

Been wanting to try out Sass? Use CSS.

That’s the first part.

The second part has to do with which structures you implement — based on known criteria and constraints.

Since you’re a startup, you have no operational history from which to make any kind of predictions or judgments. Very much unlike your employer, who does.

When they say that they’re going to build a portal for their customers — they know how many customers they have that will potentially be using that thing.

You don’t.

All of the design decisions that you make, all of the architectural considerations that you make in that scenario are based on probabilistic knowledge.

You know that if your employer has 1,000 customers, that you’re (probably) going to have to build a system that can support 1,000 users — and that you’re going to need all of the things that go into being able to do just that.

(Rocket surgery, I know.)

You’re probably going to need an administrative interface for your CSR team to help with support customer inquiries.

You’re probably going to need to have some kind of HA in place to handle the load of 1,000 concurrents, not to mention needing to be able to do updates to the system without affecting any of your active end users.

Because you’re going to have all of those concurrent reads, you’re going to need to have some read-only slave replicas — if not some cache stores — to speed things up most likely.

You’re probably going to need to have a DR in place in case anything goes wrong, as well as a comprehensive on-site and off-site backup plan.

Oh! And of course you’re going to have to have all of these layers of abstraction so that you and your team can mock anything and everything for your comprehensive suite of unit and integration tests, all while allowing you to swap out database backends (I’m sorry — persistence providers) at will!

And since it’s not just you coding the whole system, you’re going to need to have version control in place hooked into a continuous integration build platform that does automated deployments based on red/green test results so that you can do hands-off rolling deployments to this thing on a continual basis.

And let’s not forget the user management, email alerts, security, CMS, and information architecture while we’re at it!

Yes, all of that might make sense if your employer were to throw such a tasty task on your cubicle desktop.

But in Startup Land, ain’t nobody got time for that shit.

Because you don’t have any users to worry about yet!

Always Be Overachieving?

Anything you do for a user you don’t have yet makes you an overachiever.

This is where everyone goes wrong.

They forget about the goals and the criteria and constraints they need to be concerned about (and working within) when building their startup.

Goals?

The goals are easy enough to describe:

Easy enough. Let’s move on.

Because everyone gets so focused on their personal goals — and not the goals of the system — they very often completely forget the criteria and the constraints they should be thinking of.

Just a refresher: You architect, design, and build systems based on who your users are, what they need to achieve with your system, and how many of them you’re going to have.

You don’t know who your users are.

You have no idea what they need to achieve.

And you have none of them.

So why spend all this time architecting, designing, and building all of these gory gears and springs and boxes for something that no one will probably ever use?! (Not being a cynic, just applying probabilisitic reasoning here.)

The bottom line is that you shouldn’t.

And that you don’t need to.

And here’s why.

How many users do you think you’ll get in your first month of going live?

Have you ever even thought about that?

And if so, have you ever come up with an actual number?

(Hint, hint: This is one of those “criteria and constraints” I’ve been talking about…)

If you have, kudos to you!

But it’s probably still too high.

I’m going to take whatever number you’ve come up with in your head and I’m going to suggest you make it “100” instead.

Yep, 100 trial users in your first month (if you’re super lucky at that, too).

50% or more of those users will sign up (for free, of course), use your app maybe once, and then never sign in again.

Of those remaining 50 users, about 25–35 of them are going to stay active for a week or so, and, after throwing a number of ludicrous support and feature requests at you, will go dark and you’ll never hear from them again. The other 15–25 users will probably stick around long enough to (conveniently) churn out right around the time their free trial runs out.

Maybe you’ll get lucky and you’ll keep 1 or 2 of them for another month or so and pick up a tidy $29 or $49 in MRR.

But that’s reality (and one you better be prepared for).

And how much time are each of those users going to be spending in your app — actively?

(Hint, hint: That’s another one of those tricky “criteria and constraints” I mentioned…)

What? Maybe 10 minutes a week — each?

So let’s use that “sticky” 15–25 user watermark (since those freeloaders are up and gone before the morning comes anyways), and assume each of them is going to spend 10 minute a week in your app.

What exactly would you need to have in place to support them?

Not much, right?

A pretty basic CRUD app running on a boringly predictible framework like Ruby on Rails with a relational backend like MySQL all hosted on a box with a couple gigs of RAM and a core or two of modest size should pretty much do the trick, no?

You’re not going to really have to worry much about concurrency issues with 15–25 users spreading load out over your app at 10 minutes a week a piece.

Chances are your average concurrent load will be zero.

So our proposed system architecture is actually probably oversized for your current (and near future) load requirements.

You’re welcome.

You see, us developers — given the expectations that we’ve developed while working for established companies — often (ridiculously) overestimate how many users we’re going to get. And so we (ridiculously) overestimate the importance and scale of everything else.

You don’t need to start “big”.

Because you probably won’t.

Start small.

And then grow it organically.

And never build more than you have to for what you hope to accomplish in the next month or two.

Tips and Tricks…

Oh, we’re not done here yet.

Okay, so you came all this way. The least I can do is let you leave with a lollipop or two. So here are some additional startup build-out tips and tricks you might want to take to heart as you consider your startup’s architecture and design:

  • Focus on building a business — not a “startup”.
  • Design the most basic app you can think of. Then find ways to make it even simpler. Remember to keep the enterprise out of the startup.
  • Stick with technologies you know and understand. This is not “experimentation time”. The more boring, the better.
  • Don’t plan for 100 users — plan for 10. When you get 10 users, plan for 20. When you get 20, plan for 40. And so on.
  • Avoid excessive “layers” in your code. Layers take time to write, and they add more moving parts to your codebase — with no value to your end users. Keep it simple, stupid. Stupidly simple.
  • Avoid unit tests until absolutely necessary. Test your critical paths if it makes you feel any better, but know you’re testing code for users that don’t exist yet for a business that hasn’t made a dollar yet.
  • Documentation is for teams that don’t know how to talk to each other. There’s no sense in writing any at this stage.
  • Comments are meant to help you understand what the hell you were thinking when you were either too drunk or too tired to know what you were doing. Use them if you need them. Or just learn how to read code like the rest of us.
  • Add logging for things that matter. Ignore things that don’t.
  • Yes, you should use version control. Use whatever you know. There is no shame in Subversion if that’s what you’re familiar with. (Besides, nobody really understands how to use Git anyways.)
  • Use global exception handlers to catch and report exceptions that occur in your code. Just email them to yourself for now. Once you have actual users than can produce actual errors, then worry about what comes next.
  • You don’t need an administrative interface to start out. Hack records in the database by hand until it’s too painful to do otherwise.
  • If you’re going to offer a free trial for new users, you don’t need to have payment processing in place until their trial runs out and they’re ready to convert. Either process your first payments manually or wait until then to build out your payment processing integration.
  • You don’t need to implement payment processing webhooks out the gate. Just manage events by hand in both systems until you can’t take it anymore.
  • Even after that, you don’t need to have self-service account mangement for your users. Just have them email you if they want to change their account or cancel their plan and then just do it manually behind the scenes. Do this until the support burden breaks you.
  • Plan for scaling vertically — and then do that until you can’t do it anymore. Avoid premature horizontal scaling. (Vertical scaling = adding resources (memory, processor, disk, etc.) to a machine to handle load. Horizontal scaling = adding additional machines to handle load.)
  • Start with a monolith. Deploy your DB and your app server on the same box. Worry about splitting them out when your DB becomes an unwieldy bottleneck that needs its own space.
  • Make it as easy as possible to deploy changes to your code and your database without going overboard. CI and build automation is probably overkill if you’re not already an expert in it. Don’t waste your time, just deploy like you would at work. Most MVC/ORM frameworks come with database migrations. Use them. Make it as painless as possible to Always Be Shipping.
  • Again — we’re not talking about writing shitty code here. We’re talking about being realistic about what we need to be focused on. You still need to bring the best user experience you can, and it needs to work ridiculously well for your users. Don’t confuse what I’m advocating with “hacking” stuff together (although admittedly you might have to do that from time to time).
  • Remember — writing code that never gets used by an end user is the saddest thing you can do as a developer. Don’t be a sad developer. Embrace your criteria and constraints and ship something. Anything.
  • And don’t forget — your users will never see your code. Only you will. So stop trying to impress yourself.

When can I see you again?

If you enjoyed our special time together as much as I did, you should give me a follow on Medium or Twitter at @cliffordoravec. (I promise I’ll call!)

And if you’re hungry for more startup truth sandwiches like the one you just finished, you might want to check out my epic Medium series about bootstrapping a SaaS startup from scratch on your own:

--

--