`tax-engine`: The Tax Calculator That Chooses a Regime for You

A small Flask app turns Indian tax slabs, deductions, and rebates into a decision engine that compares both regimes, explains the difference, and nudges you toward the better outcome.

8 min read • View on GitHub • More from nipunworks-sde

A split desk scene with salary flowing into two tax paths. On one side, folders marked with deduction labels crowd an old filing cabinet. On the other, a cleaner path ends in a comparison panel that points to the lower liability. It explains that this repo is not just calculating tax, but choosing between two legal paths.
The real product is not arithmetic. It is the recommendation layer that compares both regimes and surfaces the better one.
Key Takeaways

The easiest mistake is to read `tax-engine` as a calculator. It is closer to a decision engine. It takes a salary, runs both Indian tax regimes, applies the legal quirks, then tells you which path looks better.

A Rule-engine based tax calculation engine.

nipunarora-git, Project Author/Maintainer · tax-engine GitHub Repository Description

The Indian tax choice this repo is actually solving

The user problem is not hard math. It is the old regime versus new regime decision, where the answer depends on deductions, rebates, and the shape of income. A spreadsheet can solve it, but only after the user already knows what to compare.

This repo makes the choice first-class. It turns the tax question into a branch: compute both paths, compare the outputs, then recommend one.

Why the engine computes both regimes every time

The repo’s key idea is parallel evaluation. It never waits for the user to guess the regime first.

StepOld regimeNew regime
InputGross salary plus deductionsGross salary
Main logicApply capped deductions before taxApply slab iteration directly
Special ruleStandard deduction and Section 80C capsSection 87A rebate when eligible
Final adjustmentAdd 4% cessAdd 4% cess
OutputFinal liability and savings tipsFinal liability and savings tips

That structure matters because it changes the product from a static calculator into a comparator. The app is not asking, “What is your regime?” It is asking, “What is the best regime for this profile?”

Under the hood: slabs, rebate, cess, and deduction caps

The Python code is straightforward in a good way. The new regime uses slab iteration over a list of brackets, then checks the Section 87A rebate threshold, then adds the 4% health and education cess. The old regime works differently: it reduces taxable income with legal caps such as `min(deductions.get("sec80c", 0), 150000)` before tax is computed.

def calculate_old_regime(income, deductions):
    taxable_income = income
    taxable_income -= min(deductions.get("sec80c", 0), 150000)
    taxable_income -= min(deductions.get("sec80d", 0), 25000)
    taxable_income = max(taxable_income, 0)
    tax = compute_old_regime_tax(taxable_income)
    cess = tax * 0.04
    return tax + cess


def calculate_new_regime(income):
    tax = 0
    slabs = [(300000, 0.0), (600000, 0.05), (900000, 0.10), (1200000, 0.15)]
    for upper, rate in slabs:
        # slab math
        pass
    if income <= 700000:
        tax = 0
    cess = tax * 0.04
    return tax + cess
A close-up assembly line where a salary number enters a sequence of gates, including a slab ladder, a rebate latch, a deduction cap stop, and a cess wheel. Two parallel tracks feed into a comparison block at the end. It explains how the engine layers tax rules instead of treating tax as one flat formula.
The mechanism is a sequence of rule gates, not a single formula.

That is the right level of complexity. You can see every rule, but the code does not bury the reader in legal prose. It expresses policy as a sequence of small decisions.

The frontend does more than display a number

The JavaScript is doing product work. `animateCount` turns the result into a live reveal. `formatINR` uses Indian numbering conventions correctly. `renderRangeTable` goes one step further and shows how the liability shifts if income moves up or down.

That makes the app feel less like a form and more like a tool. The green and red cues do a lot of heavy lifting too, because they make the comparison legible at a glance.

function formatINR(value) {
  return new Intl.NumberFormat('en-IN', {
    style: 'currency',
    currency: 'INR'
  }).format(value);
}

function animateCount(el, target) {
  const start = 0;
  const duration = 800;
  const steps = 30;
  const increment = (target - start) / steps;
  let current = start;
  const timer = setInterval(() => {
    current += increment;
    el.textContent = formatINR(Math.round(current));
    if (current >= target) clearInterval(timer);
  }, duration / steps);
}

The robo-advisor layer

The most product-like function in the repo is `get_tax_saving_tips`. It does not just say how much tax you owe. It looks at unused deduction room and turns that into plain-language advice about where savings might still exist.

That is the difference between information and guidance. A calculator returns a number. A small advisor tells you what the number means and what to do next.

What this repo is not

Category`tax-engine`Commercial tax SaaS
ScopeSingle-country salary tax decisioningBroad jurisdictional compliance
MaintenanceManually encoded rulesContinuously updated tax content
DeploymentSmall Flask appManaged platform or enterprise service
Best fitLearning, prototypes, internal toolsProduction compliance at scale
Trade-offClarity and controlCoverage and operational depth

This is not Avalara. It is not Vertex. It is not a generalized rule platform like Drools. It is a narrow, understandable system that captures one decision space well enough to be useful.

Where this fits in the broader tax-tech landscape

The comparison with commercial tax SaaS is really a comparison between two models. In one, tax content is a service you buy. In the other, tax rules are code you own. `tax-engine` sits firmly in the second camp.

That gives it a different kind of value. You get control, transparency, and easy reasoning. You do not get a massive content operation, legal coverage, or enterprise-grade guardrails.

The trade-off that defines the project

The repo’s identity is defined by a single trade-off: simplicity versus completeness. It is compact enough to understand in one sitting, yet opinionated enough to recommend a regime and surface savings tips.

That is why it works as an open-source artifact. It shows how code can help users choose between two legal realities without hiding the logic behind a black box.