Home / Blog / Article

Why Hardcoded Business Rules Cause Problems in SaaS

Published October 01, 2026 β€’ By Winrosyline Muriuki

When you’re building a software product, hardcoding a business rule can feel like the simplest solution. You know the requirement. You write the condition. The feature works. You move on. The problem is that SaaS products rarely stay that simple.

As your product grows, customers may have different requirements. Business policies change. New exceptions appear. What started as one simple rule can eventually turn into a collection of if statements, special cases, and customer-specific logic scattered throughout your codebase.

That’s when a rule that was once convenient becomes technical debt. In this article, we’ll look at why hardcoded business rules can become a problem in SaaS applications, what that looks like in practice, and how designing rules as configurable data can make your system easier to maintain and adapt.

I also explored this topic in the video below.

πŸŽ₯ Don’t Hardcode Business Rules!

Watch the video: Don’t Hardcode Business Rules! (The Wrong Way to Build SaaS)

If you prefer watching rather than reading, the video walks through the idea and why this matters when building SaaS products.


What Is a Business Rule?

A business rule is a condition, policy, or decision that determines how your application should behave according to a business requirement. For example, a payroll system might have rules such as:

  • An employee can only take leave if they have enough available days.
  • Overtime may be calculated differently depending on company policy.
  • Certain deductions may apply only under specific conditions.
  • Managers may approve requests only up to a certain limit.
  • Different companies may have different leave entitlements.

A simple rule might look like this:

if employee.leave_days >= 21:
    approve_leave()

At first glance, there’s nothing particularly wrong with this. But what happens when your SaaS has 100 companies? Company A may give employees 21 days. Company B may give employees 24 days. Company C may give different employees different entitlements depending on their employment terms. Suddenly, this:

if employee.leave_days >= 21:

is no longer a universal business rule.

It’s an assumption. And assumptions are dangerous when you’re building software for multiple businesses.


The Problem With Hardcoding Rules

Hardcoding becomes a problem when something that should be configurable or changeable becomes permanently embedded in application code. Consider this example:

LEAVE_ENTITLEMENT = 21

Every employee gets 21 days. Simple. But eventually someone asks:

“Can we give our employees 24 days instead?”

You change the code:

LEAVE_ENTITLEMENT = 24

Then another customer asks for 21 days. Now you have a problem. You could introduce conditions:

if company.name == "Company A":
    entitlement = 24
else:
    entitlement = 21

And perhaps later:

if company.name == "Company A":
    entitlement = 24
elif company.name == "Company B":
    entitlement = 30
elif company.name == "Company C":
    entitlement = 25
else:
    entitlement = 21

The application still works. But the design is getting worse.


The SaaS Problem Is Different

In a single-company application, hardcoding a business rule can sometimes be reasonable. You control the requirements. You know the organization. You know the policies. But SaaS introduces another dimension:

tenancy.

Your application may serve dozens, hundreds, or thousands of companies. Those companies don’t necessarily operate under the same policies. One customer might require:

Annual leave = 21 days

Another:

Annual leave = 24 days

Another:

Annual leave = 30 days

And another may have different entitlements based on employee category. The software needs to support these differences without requiring you to modify the source code for every customer. That’s where configuration becomes important.


Hardcoded Logic vs Configurable Rules

Let’s compare two approaches.

Approach 1: Hardcoded

def get_leave_entitlement(employee):
    if employee.company.name == "Company A":
        return 24

    if employee.company.name == "Company B":
        return 30

    return 21

This works. But the application is now aware of individual customers. That’s a warning sign. The code shouldn’t need to know that “Company A gets 24 days.” That’s business data.


Approach 2: Configurable

Instead, the company can have a leave policy stored as data. For example:

class LeavePolicy(models.Model):
    company = models.ForeignKey(
        Company,
        on_delete=models.CASCADE
    )
    annual_entitlement = models.PositiveIntegerField()

Now the database might contain:

Company A β†’ 24
Company B β†’ 30
Company C β†’ 21

The application doesn’t need to know the names of those companies. It simply asks:

policy = LeavePolicy.objects.get(company=employee.company)

return policy.annual_entitlement

Now the business rule is configurable. The application provides the mechanism. The company provides the policy. That’s a much healthier separation.


Why This Matters as Your SaaS Grows

The real problem with hardcoding isn’t the number of lines of code. It’s the cost of change. Imagine a customer asks:

“We changed our leave policy from 21 days to 24 days.”

With a configurable system, this might be an administrative change. With a hardcoded system, it could require:

  1. Changing source code
  2. Testing the change
  3. Deploying a new version
  4. Making sure the change doesn’t affect other customers
  5. Potentially creating customer-specific conditions
  6. Maintaining those conditions indefinitely

Now multiply that by dozens of customers. You can quickly end up with code that looks like this:

if company_id == 1:
    ...
elif company_id == 4:
    ...
elif company_id == 7:
    ...
elif company_id == 12:
    ...

That’s not a scalable SaaS architecture.


Hardcoding Creates Hidden Customer Dependencies

One of the most dangerous parts is that customer-specific rules can become invisible dependencies. For example:

if company_id in SPECIAL_COMPANIES:
    apply_special_calculation()

Six months later, another developer sees the code. They may not know why those companies are special. Was it a temporary workaround? Was it a contractual requirement? Is the rule still valid? Was it introduced because of a bug? Without proper documentation, the code becomes the documentation. And code is a terrible place to store information that business users need to change.


A Better Principle: Store Business Data as Data

A useful question to ask when designing a SaaS feature is:

Is this a rule that defines how the software works, or is this a value that a customer should be able to configure?

That distinction matters. For example:

MAX_LOGIN_ATTEMPTS = 5

might be application security configuration. But:

ANNUAL_LEAVE_DAYS = 21

may be a company-specific business policy. Those two values shouldn’t necessarily be treated the same way.


Configuration Doesn’t Mean “Put Everything in the Database”

There’s an important distinction here. After hearing “don’t hardcode business rules,” it’s easy to go too far and conclude:

“Everything should be configurable.”

Not necessarily. That’s another design problem. Some rules genuinely belong in application code. For example:

if password_is_invalid:
    reject_login()

The application needs logic to determine whether a password is valid. The exact implementation is part of the software’s behavior. But a value such as:

Maximum failed login attempts = 5

might be configurable depending on the product’s requirements. The goal isn’t to eliminate constants. The goal is to avoid embedding customer-specific or frequently changing business policies into application logic.


Think in Terms of Rules and Configuration

A useful architecture separates:

Application logic

The software determines how something should happen.

Configuration

The customer determines which policy or value should apply. For example:

                 APPLICATION
                      β”‚
                      ↓
             Payroll calculation
                      β”‚
                      ↓
                Read policy
                      β”‚
                      ↓
              Company settings
                      β”‚
          β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
          ↓           ↓           ↓
       Company A   Company B   Company C
          β”‚           β”‚           β”‚
        Policy A    Policy B    Policy C

The same application logic serves everyone. The configuration determines the differences. That’s one of the fundamental advantages of SaaS.


This Becomes Even More Important With Multi-Tenancy

Multi-tenant systems have another requirement:

tenant isolation.

A company’s configuration should belong to that company. For example:

policy = LeavePolicy.objects.get(
    company=employee.company
)

rather than:

policy = LeavePolicy.objects.first()

The second approach might appear harmless during development. But in a multi-tenant application, it’s dangerous. You don’t want Company A’s configuration accidentally being applied to Company B. A good SaaS architecture therefore needs both:

  • configurable business rules
  • strict tenant boundaries

Avoid Putting Business Rules Everywhere

Another problem appears when the same rule is duplicated across the application. Imagine leave entitlement is checked in:

views.py
services.py
serializers.py
tasks.py
reports.py

You might end up with variations like:

if employee.leave_days >= 21:

in several places. Now imagine changing 21 to 24. You have to find every occurrence. And what if you miss one? One part of your application may calculate 24 days while another still assumes 21. A better approach is to centralize important business logic. For example:

def get_leave_entitlement(employee):
    policy = LeavePolicy.objects.get(
        company=employee.company
    )

    return policy.annual_entitlement

Then other parts of the application use the same service:

entitlement = get_leave_entitlement(employee)

This gives you one place to evolve the rule.


Configuration + Centralized Logic

The strongest approach is often a combination of both. Don’t do this:

if company.name == "Company A":
    ...

And don’t do this either:

LeavePolicy.objects.get(...)

everywhere throughout the application. Instead:

Database
   ↓
Company configuration
   ↓
Business-rule service
   ↓
Application features

For example:

class LeaveService:

    @staticmethod
    def get_entitlement(employee):
        policy = LeavePolicy.objects.get(
            company=employee.company
        )

        return policy.annual_entitlement

Now the rest of the application doesn’t need to know how the policy is stored. It simply asks:

entitlement = LeaveService.get_entitlement(employee)

That’s easier to maintain and test.


When Should You Hardcode Something?

Hardcoding isn’t automatically bad.mThere are cases where constants belong in code. For example:

SECONDS_PER_MINUTE = 60

There’s no reason to ask a customer to configure that. Similarly, some technical constraints and invariant rules should remain in application code. The question is:

Who owns this value or rule?

If the answer is: The software itself, then code may be appropriate. If the answer is: The customer, organization, administrator, or business policy, then it is worth considering configuration or persisted data.


A Simple Decision Framework

Before hardcoding a business rule, ask:

1. Can this rule change?

If yes, consider configuration.

2. Can different customers have different values?

If yes, it probably shouldn’t be hardcoded globally.

3. Does changing it require a deployment?

If business users are expected to change it regularly, that’s a warning sign.

4. Is it business data?

If yes, consider storing it as data.

5. Is the rule used in multiple places?

If yes, centralize the business logic.

6. Does the rule represent an application invariant?

If yes, keeping it in code may be appropriate.

These questions won’t give you the architecture automatically, but they can prevent a lot of unnecessary technical debt.


The Goal Isn’t Maximum Flexibility

There’s also a trap on the opposite side. You can over-engineer a system by making every imaginable rule configurable. Suddenly you have:

Rule engine
Dynamic expressions
Custom formulas
Conditional policies
Nested configuration
Feature flags
Overrides
Exceptions

And you’ve created a system that’s harder to understand than the original hardcoded implementation.

The goal isn’t:

“Make everything configurable.”

The goal is:

Make the things that genuinely need to change configurable.

Good SaaS architecture is about finding that boundary.


Build for Change

One of the biggest lessons I’ve learned from building software is that the first implementation is rarely the final implementation.

  • Requirements change.
  • Customers change.
  • Businesses change.
  • Policies change.

Your understanding of the problem changes. That’s especially true when you’re building SaaS. So instead of asking:

“How do I make this work?”

it’s worth asking:

“How is this likely to change?”

That one question can influence how you design your models, services, APIs, permissions, and configuration.


Final Thoughts

Hardcoding isn’t inherently bad. Sometimes the simplest solution really is the right solution. The problem begins when business policies that should belong to customers or administrators become embedded in application code. In a SaaS product, that can lead to:

  • customer-specific conditions
  • repeated code
  • difficult deployments
  • fragile business logic
  • complicated testing
  • increasing technical debt
  • harder maintenance as the customer base grows

A better approach is to separate the concerns:

Business configuration
        ↓
      Data
        ↓
Business-rule service
        ↓
 Application behavior

The software should provide the capability. The configuration should define the policy. And the architecture should make it possible for that policy to change without rewriting the application every time. That’s the real lesson behind “Don’t Hardcode Business Rules.”


πŸŽ₯ Watch the Full Video

If you’d like to see me explain this concept with a practical SaaS example, watch:

Don’t Hardcode Business Rules! (The Wrong Way to Build SaaS)

Watch it on YouTube β†’


More from the journey

If you’re interested in the process of building software and learning along the way, explore more articles in Software Engineering and SaaS Building & Startup Journey.

Need a similar system built?

I build SaaS platforms, AI automation systems, and custom business workflow solutions.

Start a Project β†’