Attracted by the challenge of launching a startup and having a simple but promising idea, we decided, with my friend Alessandro, to start our own company: Abyssale.
In a nutshell, Abyssale is a banner advertising generation platform, a service that helps you gain creative control on your marketing strategy. Here is our related Manifesto.
We recently launched the minimum viable product (MVP), which took us 5 months to bring to market. From this adventure, I decided to share my experience from a technical & product point of view.
Knowing this, important subjects such as « Raising funds », « Marketing strategy » or « Business plan » won’t be covered. Obviously, keep in mind that explaining every choice I made was not feasible as it would turn this article into a heavy reading.
Don't overload! Beware of the baggage fees.
Before any development or product roadmap, it was fundamental to create a strong product vision and to be fully aligned with it.
To put this into context, this initial phase lasted a half year as our strategy was to keep our jobs by working outside working hours on the project until we were confident of its potential... or at least having reduced our chance to miserably fail.
At first, the whole concept was very blurry to us. Doing market research, iterating over product ideas, clarifying our value proposition and imagining the big picture technically helped us in defining objectives.
We trusted our intuition but also knew that we had to face the outside world and look for external opinions:
- We wrote the Abyssale manifesto and crafted a landing page. After sharing those with our network (friends, colleagues...), we started making an early user base and got feedback that comforted us on its potential (and reassured us at the same time).
Reducing the risk factors associated with a product idea before being fully involved in the project helped us to start on the right foot.
The submarine is ready. Let’s embark on the adventure
Nowadays everyone knows that theoretical ideas can be worth their weight in gold but without any strategy to reach a product/market fit, they are worth nothing. Hence, "We need a plan!"
Structure our way of working
Delivering iteratively and efficiently was crucial as we needed to get feedback as soon as possible, to be able to adapt or pivot the earlier possible.
We decided to embrace the « Lean Startup methodology » and its famous build-measure-learn feedback loop to structure our way of working, through our product development cycle.
Identify our target market to deliver a tailored product
Knowing and understanding our market is another important subject as our product is wide enough to be able to reach different targets, B2C, B2B, e-commerce, small startups…
In consequence, our strategy in defining an MVP at that time was to target « all of them », meaning containing only very simple features such as generating, bookmarking, downloading or flagging banners that everyone could use.
The definition of « Minimum viable product (MVP) » is very diverse. For the purpose of clarification, to us, it means delivering a product with just enough features to test the workability of our idea and obtain as much feedback as possible to define more precisely a market as well as a problem/solution fit.
Obviously before starting to build a whole MVP we needed to be sure the theoretical idea was technically achievable.
Oow, is it doable? From conceptualization to prototype
A prototype (or Proof Of Concept) is a very early minimum functional product that ensures the core of your idea is feasible and helps you convey your message more effectively. Defining it originates from understanding the core added value of your product. For us, it was quite simple:
Generating simple variations of banner ads for a specific product.
As a CTO, your role is crucial in its achievement. Without any previous experience in building a prototype, you will obviously feel overwhelmed, afraid of either failure or not knowing how to achieve it. This is a normal feeling, you have to embrace it, face your fears and start doing:
- Of course, there is no silver bullet.
- Try to use a technique that previously worked for you, crawling the web searching for answers, splitting issues into small steps, start developing without any plan...
- What works for me is writing down sentences that describe critical issues, mapping potential solutions (with technology) to them, sketch out graphs that represent interconnected elements, and finally start developing.
Our building prototype took us just over 1 month and looked like this:
This animated image was communicated in a marketing email to our user base. To be honest, we didn’t get much feedback. However building it reassured us of the feasibility & clarified problems we would need to face in the near future.
Focus on the (dark) technical side
Assessing the fit of a software development language (and its ecosystem) with the problem you are solving is essential. Except when you already know the solution, quickly testing several libraries/languages is a skill you need to master.
Another key element to successfully tackle a problem is finding a similar system in another domain.
For instance, a lot of ideas can be drawn from web browsers and applied to our system. Very basically, parsing a web page and then rendering it. Even if it does not fit exactly our needs, that allowed me to imagine new ways of problem resolution and define/reuse generic words that allow identifying precisely a business case (the last point represents the famous « Ubiquitous Language »).
In order to choose a development language, I tried to follow these basic guidelines:
- Keep only stable languages with a large and active community. Doing that will guarantee access to a large pool of online resources & developers for future recruitment.
- Not caring too much importance in my skills. Obviously, they will help me deliver quicker but they can be inappropriate during this phase as high-velocity and problem/solution fit are more important. Nevertheless, if a language does not provide a reasonable learning curve, forget about it.
- Find libraries that already cover my issues.
- Clearly, performance is not a concern
As I did not want to rebuild everything after the prototype phase and anticipated several subjects:
- Use foundational tools on which we can build easily on top.
- Lower the infrastructure costs: Low memory footprint & CPU usage.
- Good image handling & processing capabilities
- Machine learning support. ML can be a way to tackle the problem but as I was not sure yet, I prefer to use a language that allows scientific computations (either from libraries or the language itself).
As the product will surely evolve and change radically, taking time on code style, unit tests (or functional/smoke…) and documentation are not a priority. Code quality is also a bonus.
Where I ended up
To be fully honest, I initially did not follow all of the previous rules and ended up losing almost 1 week. I was attracted by the hype on Golang as well as its very good performance. However, after several days of development, I realized that the language itself was not adapted to my needs:
- the static typing prevents me to iterate on an acceptable rate
- the language is very verbose, above all handling nil return
- the image libraries I found were not adapted enough to my needs (some code should have been written on top to cover them)
Then I decided to give Python a try and was thrilled with my discovery:
- Straightforward & clear syntax
- Dynamic-typing allowed me to iterate quickly without any hurdle
- Image handling libraries are comprehensive and simple to use
- Optional type-hinting allows static type checking
Validating the concept technically lowered our risk of failing and enabled us to move on peacefully to the next step.
In parallel with the prototype’s development, Alessandro worked on a high-fidelity design of our product’s vision: the ideal final product. To be fully honest this exercise was too early and complicated but it confronted us with a fundamental matter:
We needed to know our users and our market better, and clarifications will come from the MVP.
A serious matter. Towards the MVP
A solution is purely abstract until you get your hands dirty and start producing it. Prototyping helped me clarify problems of generating banners:
What is a good banner ad and how could we generate it?
Nevertheless and as you might imagine, compared to a prototype, an MVP is a wonderful new world as it consists of launching a usable & lovable product.
Appropriate use, targeting a specific platform
Before getting on with defining precisely the MVP scope, the type of product that will be developed (Desktop application, SaaS, Mobile app) has to be determined.
Through a process of elimination:
- The generation of banners depends on marketing digital assets a user has to upload. As a consequence, we largely doubt those assets will be accessible on a mobile application.
- At first, a desktop application seemed very attractive as the computing power could be fully moved to the host. However, they require an initial installation and could hinder users to test our product (trust is hard to gain).
SaaS is a perfect fit. We can iteratively deliver in a very efficient way. Every user will benefit from a unified experience. It will ease our market entry.
It's time to get stronger! Scope definition
Based on the core principle that no feature targeting a specific market will be developed, the scope of the MVP was defined. We did an initial roadmap exercise and prioritized with the « Pareto Principle » in mind.
Our product should take shape in approximately 4 months and include:
- A newbie generation funnel (a form to generate banners that provides clear explications all over each step)
- An accessible user interface/experience
- Authentication & simplified authorization
- A classic 3-tier architecture (presentation, application & data layers as well as their communications bridges)
An internal back office would also be added to support and facilitate our daily work.
In terms of product features, we chose to tackle first the newbie generation funnel as it brings the most value to our product. It was defined as a wizard form (a UI design pattern meaning a series of steps to achieve a specific goal).
I imagine an ideal & complicated infrastructure, services fully orchestrated, scaling automatically depending on the traffic, using a unique language for communication, communicating to each other either synchronously or asynchronously, sending events through bus events (Kafka), asynchronous workers consuming brokers (RabbitMQ), dedicated databases (no SQL, relational) depending on the use (the famous CAP theorem helps in choosing a database technology), database replication, logging (Fluentd, Logstash), monitoring (Kibana) and alerting stack (Prometheus), and aside of them a data lake (to analyze & take actions depending on the collected data of the platform’s use).
To be fully honest, this idyllic view exists only on well-established companies. A sure thing is: that cannot be our MVP target. Instead, I tried to follow the KISS principle by keeping our platform the simplest possible:
- REST (over HTTPS) as communication constraints for our web services. Old-school SOAP or emerging technologies such as GraphQL & Protocol buffers need too much structure.
- A single relational database (MySQL, as the most used open-source relational database) with only one schema (to ease joining) to store persistent data. No replication.
- One key-value store (Redis) to cache our banners and serve them quicker (optional but truly improve the user experience)
- No asynchronous tasks (hence no workers, no bus events, no data lake)
- No direct backend service to backend service communication
- Only 2 environments (local & production only / no staging, pre-production)
Locally, Docker (as software container only) is used for each backend service to anticipate environment consistency (between developers and local/prod).
The production environment is too early to define.
Things never work exactly as planned
Backend side: The core engine
I initially thought the whole business logic would stay in a single service, build over the prototype code. However, things are not that simple.
At the end of the prototype phase, we were not satisfied at all by the quality of the generated banners. They were slightly blurred due to the raster image format we used (JPEG). Hence, we decided to switch to a generation of vectorized format (SVG).
The constraint of being able to generate on the backend part a self-sufficient (without any external HTTP calls) SVG file drove us to convert texts (with a specific font) to SVG Paths. Except by using low-level font libraries and coding from scratch the transformation, I found only one library that covers it.
From this unfortunate reality, I decided to clearly separate concerns:
Generation of banners (Python service / Falcon - API framework / Gunicorn - WSGI server):
- As this is the core of our product and contains most of our business complexity, it has to be adequately designed. Sticking to the Domain Design Driven (DDD) approach allowed me to precisely define the responsibility of each component & represent business problems/solutions into code. Obviously, DDD can increase the delivery delay and has to be limited to a certain limit to keep velocity high.
- It must be dimensioned depending only on the numbers of banners generated
Rendering of banners & user actions
- I considered it as being ephemeral as the user actions will surely change/evolve dramatically between the MVP and the next version.
- As always, in the process of prioritizing my efforts, it is of lower quality than its Python counterpart.
- It must be scaled depending on the number of users, saved & generated banners.
Presentation layer: The representation of our brand
We choose to build a single-page application (SPA), to offer the smoothest user experience.
I had no previous background in building SPA, just an experience in building from scratch a JS script to display multiple advertising banner formats on websites. To reduce risk, and after lots of reading, I choose the biggest ship out there (very active release process, huge community and very trendy), React.
The famous « Create-React-Application » (CRA) project from Facebook allowed me to create a new application without the hurdles of configuring environments, asset bundler (Webpack !), ECMA/JSX transpiler (Babel) and on top of that several production optimizations are already tuned. On the visual part, the « Material-UI » React framework was a perfect fit to start.
Similarly to the backend part, I tried to make the frontend part as simple as possible, TypeScript and state management libraries (Redux, MobX) seem very attractive technologies/libraries to use but I stayed out of them as they need skills that I didn’t have at that time and can bring new issues and increase code complexity.
Authentication & simplified authorization
It brings almost no value to our product but cannot be underestimated as sensitive data (user privacy) are involved as well as our brand image. I also know this initial choice will likely follow us for a long time because migrating from one solution to another easily is always complicated.
As I did not want to spend time on this subject, I chose the Auth0 service. It provides a free plan, login and signup widgets, connections to social identity providers (Google, Facebook...) and JWT tokens that can be used to authenticate a specific user & authorize him limited actions.
To be honest, If I had to do it again I would probably not use this tool as the path was very complicated to make it fully working (and is a very expensive service once the free plan is no adequate anymore, such as using a custom domain to embed the login on our side and not facing Cross-origin resource sharing (CORS) issue).
Our internal back-office
On this side, the only thing that mattered was the functional scalability of the solution.
We had 3 options:
- Building it from scratch
- Using an admin framework and manually hosting it
- Using a SaaS such as Forest.
Not knowing SaaS services well, we chose the second option (by using react-admin) to reduce any risk related to a non-available or priced feature for instance and to accelerate its delivery.
For now, this choice fully fulfilled our requirements.
A new member joined our founding team
Remy joined us after 2 and a half months as a founder. His DevOps skills have been crucial in delivering our production infrastructure.
But first, several things were done before the production setup:
The code of every project was copied on a remote git repository (that could have been done earlier, Shame on me !). Gitlab was chosen as it provides unlimited users as well as a limited but free CI/CD.
His learning curve had to be the quickest possible. As I needed to explain the project, problems & solutions we faced/are/will be facing, I had to take a short break.
This “break” in the development allowed me to get back on my feet, analyze what has been delivered, put words on theoretical concepts I only had in my head. That also helped us to discuss & define global orientations that would shape our product for the future.
At first, I imagined using a Cloud PaaS (Platform as a Service) (vs Cloud IaaS or on-premise), such as Google App Engine or Amazon Elastic Beanstalk, to reduce the maintenance cost, accelerate our delivery and be able to scale easily depending on the traffic you get without any knowledge/skills required on the operational details (networking, CPU, memory, storage...). The drawbacks of using those are:
- the final cost that can be expensive and is difficult to know in advance as it is a pay-per-use pricing model
- the huge dependency you get upon a single Cloud Provider (known as vendor lock-in).
Remy came with the idea of using a Cloud-managed Kubernetes (the container orchestration system). I was quite reticent about the idea, as I had no previous experience with it and we would have to maintain our servers & databases.
After some discussion, the capabilities:
- of being able to switch from one provider to another without too many difficulties in the future was very appealing
- Our Docker images could be pushed to a remote registry and used as it is easily
Knowing he already embraced this technology in production (with microservices) convinced me to go in this direction.
We got 5k $ of AWS credits from our ProductHunt Ship subscription, hence our choice of the cloud provider was quite simple :)
Still, I don’t recommend this option at all during the MVP phase if you are not experienced with the technology. Even though it is not the quickest way to reach a production-ready environment, it brings an incredible evolving system.
Tackle the last 20%
About one month before the planned delivery date, 80% was delivered and we knew this phase would be complicated as it requires to fine-tune everything:
- Adding Slack connectors to the feedback & contact us submissions. This ensures a better user support reactivity, almost in real-time.
- Setting up Hotjar & Google Analytics
- Reviewing the MySQL database, to ensure the nomenclature & indexes were correctly defined. As you know, once in production, migrating schemas/tables/columns is a little more touchy.
- Configuring logging levels in all our applications to reduce noise.
- Ensuring the security of our backend services (using JWT tokens as well as configuring CORS)
- Polishing the interfaces and add Sentry to track any frontend errors
- Reviewing a maximum of possibilities during generation and tackling bugs when found.
- Changing our invitation process as it is extremely important to give a very good experience during the first interaction with your brand.
Phew, finally! Ready to launch \o/ One week late compared to the delivery date we planned 4 months earlier.
Wow, such a wonderful landscape :) The MVP launch afterward
Of course, even after a very thorough Quality Assurance (QA) phase, bugs always occur and have to be fixed.
Simultaneously, the product was marketed on several websites, which allow us to get traffic and feedback.
We knew that several features had to be improved and we voluntarily removed some of them in this release (better ship fewer features than too much). The feedback we got fitted almost exactly what we predicted and allowed us to define more precisely our target market, and the future evolution of the product.
At that time, we took the time to analyze and categorize each feedback, we then initiated a 3 days full-time discussion/ brainstorming session in order to define the direction we had to follow for the beta product.
The role of the CTO in an early stage startup
The role of CTO is an evolving one: responsibilities and tasks are not the same depending on the phase the company is in.
From my point of view, in a very early stage startup, this role is about being able to choose technologies, deliver quickly & efficiently… The skills needed are different from another stage of a startup where leadership & management is expected:
- Ability to understand the requirements of finding an appropriate programming language
- Responsible of a very wide spectrum (front end, back end, dev ops, product) and at the same time be fully aware, understand & participate on every other part (marketing, business, communication)
- Stay focused & motivated no matter what happens, no one is managing you.
- Don’t be afraid of using a language/framework you don’t master, fast learning has to become your best asset/skill
- Obviously, as communication with your colleagues is key, context-switching is another important asset to have
- Make your choices wisely and be as neutral as possible
- Take responsibilities
- Refactorize often… very often…
In my opinion, the role of CTO at this stage is close to a mix of full stack developer and product manager.
Into the Abyss, so many things to discover !
Building a minimum viable concept on the technical side is a matter of choices depending on the project, the ambition, the technical experience & skills of the startup team. Those decisions, as you can imagine from this article, must be made carefully as they can cause big delays in product delivery.
As a result, there is no silver bullet which fits all. The only advice I can give is to shape your product based on the strengths of your founding team.
I think we reached our first milestone successfully, but the hard part remains to be done. First successfully monetizing our product by choosing the right business model (and pricing), then making it scale and at the same time keeping healthy retention.
👏👏 you have successfully survived this article !! :)
If you have any question whatsoever with this story, do not hesitate to reach out to us at Abyssale.com