Software Development for Early-Stage Startups

Steve Mushero
20 min readJan 17, 2023

Some advice for non-technical founders on getting started to MVP

This is part of my series on DevOps for Early Stage Startups, and is designed for non-technical startup CEOs who don’t have a CTO nor senior technical help. They are often building MVPs with outsourced resources, hacking things together as best they can to get going.

Then they get some investment and need to scale up a bit, be more professional, and get off to the races … this article series covers some things to think about, and some strong opinions.

Software Development

For most early-stage tech startups, software development is the core process they must master, and the sooner they get on the right road, the sooner they can quickly & consistently deliver features to their users.

The trick is to do this in a way that matches your stage and resources, as Google’s processes are hardly useful to a 3-person company. However, there are many basic good practices that even a single developer should get going from the beginning, as this really avoid later problems, and makes development both better and faster.

This guide is not about how to write software itself, as there are plenty of books on that, but instead, about all the ways I see startups make mistakes, often ones that take years to unwind or cleanup.

Getting Started

Writing code is, in many ways, simple — get a private GitHub account, a development environment, and write code! And you should start that way, as getting going is important, but don’t forget to also chip away at other suitable best practices, outlined below.

Architecture

Software and system architecture is highly variable, depending greatly on the problem being solved, where the system will be deployed, the scale of the system, the experience of the team, and the technology stack involved.

Below is some general advice, though the overall rule is KISS, keeping it simple. The simpler, more mainstream, and straightforward your system is, the easier it will be to build, manage, and evolve. In fact, the more boring your code and systems are, the better.

Programming Languages

Choosing a programming language is often a religious war, but generally, you should choose from a very small list of the most common languages and environments. Using a popular language will make it much, much easier to hire, find tools, use 3rd parties and 3rd party libraries, etc.

There are many sexy languages to probably avoid, such as Erlang, Smalltalk, Koitlin, Haskell, or other cool, but very hard-to-staff languages. And while Java is good, you should probably avoid it unless you have a very good reason, such as enterprise software.

PHP is still common, but Go is taking over for lots of applications (though not always for web frameworks). C#, Ruby, PERL, and others have faded from the scene for new systems, and Python is not as popular as it once was (though still the standard in data science and Machine Learning).

In practice, most new systems are built in JavaScript/Typescript, Go, Python, PHP, and/or Java. Don’t stray far from this list.

Regardless, try to use a good framework, like SpringBoot for Java, Laravel for PHP, etc. that provides dozens of built-in services and best practices, especially for security, database handling, logging, etc. And a front-end framework such as React, Vue, etc.

Also, try VERY hard to standardize on one backend language (your frontend will almost certainly be JavaScript). Do not let developers choose different languages for different services or functions, or based on their backgrounds, as this creates an unmanageable (and un-staffable) mess for small companies. Just use one language for all your backend work, really.

This means picking something and sticking to it, even if it’s not great for every use or role you have (e.g. I’ve written a lot of batch systems in PHP; not because it’s good for that, but because our core system was PHP, and thus our developers needed only one skill set; it worked & scaled just fine).

Note mobile applications for the iPhone and Android are whole separate ecosystems, with their own languages, tools, and processes that are outside the scope of this guide. As always, find and follow best practices.

Esoteric Services

Avoid fancy new or esoteric services like the plague. This includes the latest databases and other systems that are on the leading, or even bleeding, edge, unless they add some very unique value to your solution (unlikely).

New and esoteric services rarely live up to their claims, but they really complicate software development and especially deployment and operations. They are also hard to hire for, and no one has experience in how to deal with them, especially later at scale. Avoid them.

Microservices

Avoid microservices in early-stage systems, as they are not worth it. They greatly complicate development, destiny, deployment, and troubleshooting are not something you should generally mess with.

Build a so-called monolith, or at least only break up your system into large macro chunks that make sense, but do not overdo these splits. For microservices, wait until you have product-market fit, a large team, and more complex needs.

Never Build What You Can Buy

You should also not build what you can buy, especially in these days of plentiful SaaS and other services that are everywhere, often for free or at low cost.

Almost everything we used to have to build is now at your fingertips. And you can always build your own later when your scale or needs exceed what’s easily buyable (by which time you’ll have more resources to build it).

This includes infrastructure and services, but also very code-application things like user management, authentication, chat, video, email sending, and many, many other things

Off-Shoring Development

Many startups outsource some or even all of their early-stage development, and many push this offshore to save money. This is fine, but there are some additional considerations when using outside development companies, see below. A key aspect is always makes sure you control the outsourcing company, and that they do not control you (or your code).

Code Ownership & Control

A critical element of any 3rd party contract and arrangement involving software is who owns the code and who controls access to it. Most 3rd party contracts are pretty good about who owns the code, but be sure you own it for real, and the 3rd party or offshoring company has no rights to it, now or ever (i.e. they can’t use it in other projects, etc.)

More important is where the code is and who controls it. It’s very common for 3rd party developers to set up your first git code repository, owned & controlled by them, but this is a very bad idea as they can withhold access, etc. if there is a payment or other dispute.

Always make sure the code resides in your repository and your account, at github.com, gitlab.com, etc. There should be no exceptions to this, right from day one.

Starting Dev Tools

As mentioned above, to get started, just get started with a development environment and private code repository, e.g. GitHub and a development environment and basic tools (like Gradle, Maven, webpack, etc.) and go from there. Nothing fancy needed at the beginning.

Development Processes

Different companies will use different processes to build software, based on the founders’ experience, but all will generally be so-called Agile processes.

This means rapid and flexible software development and very frequent deployment, with very short development cycles, from hours to days, so new code is designed, built, and deployed much more rapidly than in traditional processes.

The key is to really be agile, and to use a very simple process that works for you. Many companies start with Scrum processes, but these are falling out of favor due to their complexity and overhead. Instead, try a simple Kanban system, which maintains simple lists of the next high-priority tasks in sorted order, frequently updating the tasks and priorities.

Review your team’s tasks every day or two, make sure the small team is working on the right things, getting rapid feedback, running tests, deploying, etc.

Development Tracking

It’s important to track and manage your development work, even for a single developer. Plus, you’ll find it much easier to expand your team and scale if you start off with a good system.

By far the most common is the Jira system, which creates simple issues or tickets for everything you want to do: features, bugs, enhancements, etc. and gives them a priority, owner, assignments, etc. Other popular systems include Trello, Asana, and Pivotal. You can also use GitHub Issues for basic bug and issue tracking, but it’s often too simple as you have a real team, numerous tasks and priorities, etc.

Whatever system you choose, use it religiously to enter & track ideas and every task, as you can document how and why things happened, bug notes, etc., and the issue numbers are used through the development system (e.g. branches, merges, and releases) to know what was done and fixed when, where, and by who.

CI/CD Systems

The CI/CD (Continuous Integration / Continuous Delivery-Deployment) concept means different things to different people, and can be used in different ways.

As a startup, you want to focus on a few core tasks you need to be done — the goal is a simple one-click centralized build and deployment system — this will greatly speed development, adding developers, plus it forces all sorts of best practices along the way.

CI — Continuous Integration

CI systems continually merge in, build, and ideally test your code as it’s committed to your repositories. This forces developers to deal with conflicts, failures, and problems on a near real-time basis, while things are fresh in their minds, with the goal to ensure the code is always as clean, stable, and functional as it can be.

Developers should be committing and merging their code as often as possible, usually at least once per day for commits, and every day or two for merging. This forces them to deal with other developers’ code, conflicts, etc. right away.

Initially, developers will also build various ways on their own laptops, but this should be rapidly standardized with build tools such as Maven, Gradle, etc. which then everyone uses across the team.

And while developers can build locally, you’ll want to migrate the process to a centralized build system as soon as you can, connected to your git repo, so the process is run automatically for all new code as it’s checked in or merged. Note developers will also continue to build locally for many years to come, as they need this to do local testing.

Automated builds are usually done via an automated build system, such as Jenkins, CircleCI, GitHub Actions, TravisCI, and others. These platforms are the key to your CI/CD system, and lots of folks start with Github actions, as they are free and already integrated if you use GitHub.

Be sure you set these systems up correctly and securely, as there are many common mistakes made that create vulnerabilities over time.

Docker & Containers

Developers will likely be using Docker Containers for much of their work in modern systems. This is a complex area, but generally, containers are a very good thing, vastly simplifying the build, configuration, and deployment processes for systems today. In fact, most systems use containers to build & test the application, which is then deployed via containers.

More on this, below.

Code Checking

More advanced CI systems will run a number of code-checking tools to help check for common mistakes, security issues, formatting problems, etc.

These all vary highly by language and technology stack, but it’s good to start introducing the basics as soon as you can. It’s especially important to scan the code for hard-code security credentials and keys, which should NEVER, EVER be in the code, configuration, or any checked-in file. And if a credential ever gets checked in by accident, change that credential immediately (i.e. change the password, issue a new key, etc.)

Supply Chain Checking

Supply Chain Security is an important new and developing area that you’ll see mentioned. This is making sure the myriad of 3rd party libraries and code you use in your software is safe, secure, and up to date. Over time, this becomes very important.

This is a complicated and evolving area, and many services such as GitHub have some capabilities in this area. Overall, this is something to look at relatively early in your process, especially for the more challenging ecosystems such as JavaScript (which nearly everyone uses). But don’t over do it.

Code Testing

Developers should include unit and other tests in their code, and ideally for higher-level features, white/black-box and other testing, too. The CI system normally runs as many of these tests as is practical (at least all the unit tests), though this is a complex area — you should at least set it up to run the unit tests on every commit or merge.

There are also many third-party sites and tools that can run tests, especially for the UI, etc. but these get expensive and complicated, so their use depends on your situation.

CD — Continuous Delivery-Deployment

The second part of CI/CD is the delivery or deployment phase, usually bundled in the same CI/CD tool, and focused on deploying the built and tested the application to its environments, such as for dev, testing, or eventually, production.

The normal progression is to build every code merge and deploy it to a development environment as part of Integration testing, then later deploy it to a QA or testing environment for users to look at, and finally, to production for real users. This may vary based on your situation.

Ideally, you will be deploying the exact version of the application, container, etc. in all these environments, but this is often not practical as the dev/test versions are usually built with debug flags, Javascript comments, etc. that you don’t want in production, but you should still strive to build and deploy as identical versions as you can, to ease in production troubleshooting.

Security, Users, and Keys

A security mindset and culture is critical to long-term success. You really want good security & security practices baked in from the beginning, or you’ll be cleaning it up for years to come.

The absolute rule, to be observed above all others, is NEVER SHARE usernames or accounts among your team. Many startups share users & passwords, including with their 3rd party developers or part-time staff, and this creates endless security risks and other nightmares later. Do not do it.

For EVERY service, system, and tool you use, create a separate username and random password (or key) for it. Save those in a password manager such as 1Password. This will make your life so much easier later, especially when your early teams leave and go on to other things. In 1Password and similar systems, you can create different vaults, such as for developers, finance, etc. and put the root/admin credentials in a separate, tightly-controlled vault only open to founders & very key staff.

And when someone leaves, make SURE to disable these users in every system — this is very hard to do in practice, as you will easily have dozens of systems to keep track of even in your first few months. But at least be sure to remove users from your code, build, docs, and infrastructure systems ASAP when they leave. And check those systems every month or so for lingering users that need purging.

On a related note, you must never, ever let developers hard-code security credentials, passwords, API keys, or other secrets in the code or anything that is checked in, ever. Just never do it. Always use external configuration, such as environment variables (usually the easiest to start with), or a secrets manager. This is such a common mistake made when people are in a hurry that you must continually guard against it (and later automate checking).

Two & Multi-Factor Authentication (2FA/MFA)

You should enable and use 2FA wherever possible, especially for internal use and developers. This may not be available on all services and can get annoying if it’s needed 25 times per day, so you have to balance security vs. annoyance factor. But at least enable it on key infrastructure and administrative accounts, i.e. any user that can cause damage.

GitHub

The first thing you need is a git repository, which today is usually done on github.com, though some people prefer the more powerful gitlab.com system; they are quite similar, and both have free plans you can start with.

Whatever you use, make sure it’s a PRIVATE repository, with SEPARATE users for each person, system, or product that will access it.

You can decide how many repositories you need, but separate ones for the frontend, backend, and infrastructure are common when getting started (this greatly depends on your tech stack).

Control access via github/gitlab users in groups (e.g. ‘developers’) so you can easily remove access when they depart. You generally do not need to grant individual permissions, except for GitHub Actions which need higher level permissions.

Branching

When and how to branch your code is often controversial, with no best way, though feature branches and merges to dev, test, and main are probably the simplest to start with. This is called the GitFlow process.

Regardless, if you branch, try to name branch and merges with some standards, usually and especially including the task or ticket number when you are using a task manager such as Jira (which you should be doing). This will greatly ease the management of the build, merge, deployment, and tracking processes over time.

README.md

Fall in love with README files in your Git repo — write them for key developer things, especially how to set up a new development & deployment environment for each new developer; this will save much pain later. Your goal is for a new developer to be able to get fully setup and able to build & run based on your README.

Also create a README for the system environment variables, feature flags, and other options that are needed to run and configure it. Keeping these up to date will save a lot of time.

DevOps Tooling

Broadly, your tooling is up to you and has to fit your technology stack, comfort level, and where or how you will be deploying (more on that below). But to get started, start with the tooling you know such as a development environment, possibly a build environment like Jenkins or other CI/CD, Docker if you’ll be using it (you probably should), and so on. More on this, below.

Deployment

It is very important to be deploying your application to a cloud environment as early as possible, ideally right from day one. Do not just test or look at the system on developers’ laptops, as this will hide all sorts of problems that will slow you down later, especially as the team grows and others need to see or test the system. Build & deploy on real servers.

You may not have the resources to build a full beautiful deployment system right away, but do get your developers in the mode of committing, merging, building, and deploying to some server somewhere, as that process will force lots of best practices early on.

This is especially important with 3rd party developers — do not let them develop for weeks or months before building, deploying, and showing you a running system. It’s easy to let them build for a while and promise things later, but you must see things really running as early as possible.

This also forces the team to work on how and where things will be developed, including how to configure the different environments, avoid hard-coded elements, etc. Otherwise, they’ll have to figure this out later, leading to significant delays and cleanup.

Slack

Everyone uses Slack, often for everything. In concert with email and perhaps a few other whiteboard or documentation systems, it’s often the core day-to-day communication channel. This is especially true for today’s remote workforces, as Slack becomes the common water cooler for everyone.

Much has been written about Slack and how to use it, and for early-stage companies, it’s probably wise to use it as much as possible, with channels by department and team.

The threads feature is especially helpful for discussions, though it gets overwhelming when there are dozens of things going on, so try to close out threads as quickly as possible.

Keep in mind that Slack is a little hard to search, depending on your subscription plan, and is not as good as email for summarizing and documenting the how and why of various decisions, especially years later when you are want to look back at something.

This means summarizing things in an email from time to time, especially when the Slack discussion was in a small team, but you then want to tell others about it. It also means summarizing how/why things were fixed in your ticket system, e.g. Jira, for future info.

Many of the tech teams’ tools, such as CI/CD, monitoring, etc. will integrate with Slack, which can be a good idea but can also get noisy. However, they are easy to turn on/off, so if it makes sense, go ahead and enable a few as you go.

You might consider SlackOps, which often uses robots and cute tools to do things automatically, but these are hard to do well or securely and are best left to much later.

Documentation

Decide early on where you want to main developer and system documentation. This can vary a lot, from markdown files in the repo to Google Docs to Wiki systems like Notion.so — ideally you can stick to one place and format for docs, though these tend to vary over time — the most important thing is to get used to writing things down, tracking the system physical and logical architecture, how things work, a team directory, build and deployment process, etc.

YAGNI — You Aren’t Going to Need It

You should start doing lots of best practices and building up things as you go, as outlined below, BUT you should not overbuild or get stuff you don’t need. Known as YAGNI, or You Aren’t Going to Need It, it’s the sin of overbuilding infrastructure, over-designed features that are ways out in the future, etc. Don’t over-build, and don’t over-engineer. As in all things, balance is your friend.

Ownership & Possession of Code

It’s critical that you set up and own all your own SaaS and service subscriptions, especially for anything software development, code, and cloud-related things.

It’s very easy for your part-time or 3rd party developers or others to do this for you, in their own accounts to just get going, but that’s a very bad idea, as they can withhold access, etc. if there is a payment or other type of dispute. Do not let others own anything or you’ll likely regret it. Never use personal services, period.

If developers or 3rd parties ask for services, SaaS tools, etc., get a list of what they need and then set up your own accounts, creating users for them while you retain (and keep secret) all the root or admin passwords — this can be a challenge for non-tech founders, so get trusted help if you need it.

Developer Data & 3rd Party API Services

Modern applications rarely stand alone, instead depending at least on a database or two (and their data), and often many 3rd party services (APIs) as part of best practices.

However, this greatly complicates the developer experience, as they often need most, if not all, of these services available to build and test their code each day.

For 3rd party APIs, there are several ways to manage them, and a very important aspect is to have DEVELOPMENT accounts, keys, and environments for all your 3rd party services.

This is so developers don’t need production service access, and to make sure they are never seeing, nor modifying, production data (and maintaining GDPR and other data compliance). Sadly, not all 3rd party systems have developer modes, so seek out ones who do where you can.

Teams usually find a very wide range of 3rd party services for any particular need, ranging from nearly free to expensive enterprise-grade services. Try to pick quickly, but wisely, leaving a bit of room for expansion, but also try to use what’s simple, inexpensive, and easy-to-implement today — you can evaluate and upgrade later, but getting going now on a modest budget is usually best.

Note, developers may also need mock services, such as email sending, so developers can test sending emails or notifications that never actually goes to real users (which would be embarrassing). Services like MailTrap work well for this.

Local Services & Data

Developers often run a small number of local services, such as databases, usually in Docker Containers. The availability of containers on the laptop (and plenty of RAM) has greatly simplified this process and is popular for running things like MySQL, ElasticSearch, and many other 3rd party products.

The challenge is how to get good data (and data structures) into these local databases — ideally, the code has automatic structure migrations for this (which is needed for production and automated testing, anyway).

For local development databases, some companies use a copy of the production database for development and testing, but this is a very bad practice, as it creates security risks, and often violates rules such as GDPR and CCPA regulations on data privacy. If that laptop gets lost, suddenly all your company’s data, often including private customer info, is out in the wile.

It’s much better to either use mock test data or use a scrubbed and anonymized (remove all PII data, e.g. names, addresses, emails) database extracted from production. This is sometimes hard to do for every developer, so many teams use data shared services, below.

Shared Services

More complex systems often need bigger database and other services that are hard or impractical to have on every developer’s laptop. In this case, teams will usually deploy shared databases, etc. to a private development environment that developers can reach via VPNs, ssh tunnels, or great networking tools such as Tailscale. A development environment is usually provisioned for this, so the developers and the deployed development stage code share the same data.

Developers also often need public 3rd party APIs, such as for billing, sending mail, authentication, mapping, and many other things. As noted above, try to create dev/test environments on those 3rd party sites (the best ones allow this), with separate users or keys for each developer, ideally with limited permissions for only dev/test data.

Testing & QA

You need to test your software, or your users will be testing it for you. The challenge for early-stage startups is good testing resources are expensive, and automated testing is complex and expensive.

The best scenario is a dedicated testing team that tests each feature, bug fix, etc. after each is merged in and deployed to the dev or test system, providing feedback and status info via the ticketing system such as Jira.

However, the usual early-stage scenario is no staff nor money for QA, so the product owner, management, and other staff often test as best they can. That’s fine, as long as they document the bugs and how to reproduce them, etc.

Regardless, it’s critical that developers test their fixes and features as best they can. Sadly, many do not test very well, leaving bugs to be found by testers, product people, or users, and delaying real fixes by days, weeks, or more. You should insist that developers test fairly extensively, and follow up with them when bugs are subsequently found, in an effort to avoid future recurrences.

One recent useful tool for UI testing that can be used by nearly anyone is RainForestQA, which can do basic login, click, feature test, data entry, etc. Like any automated QA tool, you’ll need to work to keep the tests up to date with all the frequent changes your team makes.

Conclusion

This concludes part 1 of this series, having covered some challenging areas where many early-stage startups with non-technical founders make mistakes.

Steve Mushero is a Fractional CTO for Silicon Valley and other startups. He’s been a founder, CEO, CTO, Architect, and consultant over the years, along with writing books, speaking, flying helicopters and more. He’s easy to find on the Internet, including at SteveMushero.com

--

--

Steve Mushero

CEO of ChinaNetCloud & Siglos.io — Global Entrepreneur in Shanghai & Silicon Valley