Stop Using Machine Learning for Everything: Why Decision Models Will Save Your Production Pipeline

We've been hammering every business logic nail with a probabilistic machine learning sledgehammer. It's time to talk about when deterministic decision models actually crush ML classification.

On this page
  1. The Core Dilemma: Deterministic vs. Probabilistic Systems
  2. Architectural Deep Dive: Building a Hybrid Pipeline
  3. Step 1: The ML Classification Layer (Risk Scoring)
  4. Step 2: The Deterministic Decision Model Layer
  5. Step 3: Executing the Pipeline
  6. When to Choose Which: The Engineering Matrix
  7. Conclusion: Stop Chasing Hype

Stop Using Machine Learning for Everything: Why Decision Models Will Save Your Production Pipeline

The startup pitch sounded incredible: “Our new deep-learning classifier predicts user churn with 94% accuracy!”

Three months later, the model was in production. Then, the compliance team walked into the engineering bullpen. They had one simple question: “Why did the system flag our enterprise client’s account for automated service termination last Tuesday?”

The lead data scientist stared at the TensorBoard graphs, muttered something about multi-head attention weights, vector embeddings, and black-box decision boundaries, and realized they had no idea.

Welcome to the modern engineering trap. We have been conditioned to believe that if a problem can be solved with code, it should be solved with probabilistic machine learning. Need to route support tickets? Train a transformer. Need to calculate dynamic loan eligibility? Throw a gradient boosting machine at it. Need to evaluate compliance rules? Let’s fine-tune a large language model.

This is an architectural disaster waiting to happen.

In this deep dive, we are going to tear down the false dichotomy between Decision Models and ML Classification, examine the hidden architectural costs of probabilistic systems, and write actual code to show when you should stop training models and start writing explicit decision logic.


The Core Dilemma: Deterministic vs. Probabilistic Systems

Before we look at code, let’s define our terms with rigorous architectural clarity.

  1. ML Classification: A statistical approach that maps input features to a discrete set of classes based on learned patterns from historical data. It outputs probabilities (P(y∣x)P(y|x)) and relies on inductive generalization.
  2. Decision Models (e.g., DMN - Decision Model and Notation, Rule Engines): A deterministic, explicit representation of business logic, regulations, or workflows. Given a set of inputs and a structured decision table or decision tree, it yields a 100% predictable, explainable, and auditable output.

The industry-wide failure mode is treating ML classification as a universal problem solver. ML excels at perception (speech-to-text, computer vision, semantic clustering) and prediction under uncertainty (forecasting demand, anomaly detection).

Decision models excel at governance, policy enforcement, and deterministic business logic where rules are defined by human lawmakers, contracts, or compliance frameworks.

+-------------------------------------------------------+
|                    The Problem Space                  |
+---------------------------+---------------------------+
|                           |                           |
|   PERCEPTION & PATTERNS   |   RULES & GOVERNANCE      |
|   - Image Recognition     |   - Regulatory Compliance |
|   - NLP Intent Parsing    |   - Loan Eligibility      |
|   - Anomaly Detection     |   - Tax Brackets          |
|                           |                           |
|   --> USE ML CLASSIFIERS  |   --> USE DECISION MODELS |
|                           |                           |
+---------------------------+---------------------------+

When you use an ML classifier for deterministic business logic, you inherit massive operational overhead:

  • Drift Maintenance: Data distributions shift, requiring constant retraining.
  • Explainability Deficit: GDPR and financial regulations (like the EU AI Act) penalize black-box decisions.
  • Testing Fragility: Unit testing a probabilistic model is notoriously difficult; assertions must be written as statistical tolerances rather than exact outputs.

Architectural Deep Dive: Building a Hybrid Pipeline

Let’s look at a real-world scenario: Fintech Loan Underwriting.

If we build this purely with an ML classifier, we feed user metrics into an XGBoost model and output an approval flag (0 or 1). If the model fails, we have a compliance nightmare.

The correct architectural pattern is a Hybrid Pipeline. We use an ML classifier to score predictive risk (e.g., propensity to default based on spending habits), but we pass that risk score alongside hard financial metrics into a deterministic Decision Model that executes regulatory policy.

Let’s implement this in Python using a rule engine approach alongside a standard scikit-learn classifier.

Step 1: The ML Classification Layer (Risk Scoring)

import numpy as np
from sklearn.ensemble import GradientBoostingClassifier

class RiskClassificationModel:
    def __init__(self):
        # Simulated pre-trained gradient boosting classifier for default risk
        self.model = GradientBoostingClassifier(random_state=42)
        # Mocking training for demonstration
        X_train = np.array([[300, 0.8], [700, 0.1], [550, 0.5], [800, 0.05]])
        y_train = np.array([1, 0, 1, 0]) # 1 = High Risk, 0 = Low Risk
        self.model.fit(X_train, y_train)

    def predict_risk_probability(self, credit_score: int, debt_to_income: float) -> float:
        """
        Outputs the probability of default.
        """
        features = np.array([[credit_score, debt_to_income]])
        prob_default = self.model.predict_proba(features)[0][1]
        return float(prob_default)

Step 2: The Deterministic Decision Model Layer

Now, instead of letting the model make the final binary decision (Approve / Deny), we pass the model’s output—along with strict regulatory and business rules—into a decision table processor.

from dataclasses import dataclass
from typing import Literal

@dataclass
class LoanApplication:
    applicant_id: str
    credit_score: int
    debt_to_income: float
    requested_amount: float
    has_bankruptcy_history: bool

@dataclass
class DecisionResult:
    status: Literal["APPROVED", "REJECTED", "MANUAL_REVIEW"]
    reason: str
    risk_score: float

class LoanDecisionModel:
    def __init__(self, risk_classifier: RiskClassificationModel):
        self.risk_classifier = risk_classifier

    def evaluate(self, app: LoanApplication) -> DecisionResult:
        # 1. Hard Stop Rules (Deterministic - No ML allowed here)
        if app.has_bankruptcy_history:
            return DecisionResult(
                status="REJECTED",
                reason="Automatic rejection due to bankruptcy history.",
                risk_score=1.0
            )
        
        if app.credit_score < 500:
            return DecisionResult(
                status="REJECTED",
                reason="Credit score falls below absolute floor of 500.",
                risk_score=1.0
            )

        # 2. Invoke ML Classification Layer for probabilistic risk
        risk_score = self.risk_classifier.predict_risk_probability(
            app.credit_score, 
            app.debt_to_income
        )

        # 3. Deterministic Threshold and Policy Logic
        if risk_score > 0.75:
            return DecisionResult(
                status="REJECTED",
                reason="High predicted default risk based on behavioral metrics.",
                risk_score=risk_score
            )
        
        if 0.40 <= risk_score <= 0.75 and app.requested_amount > 50000:
            return DecisionResult(
                status="MANUAL_REVIEW",
                reason="Medium-high risk score paired with high loan capital request.",
                risk_score=risk_score
            )

        if app.credit_score > 750 and risk_score < 0.20:
            return DecisionResult(
                status="APPROVED",
                reason="Prime tier applicant meeting all deterministic and probabilistic criteria.",
                risk_score=risk_score
            )

        # Default fallback rule
        return DecisionResult(
            status="MANUAL_REVIEW",
            reason="Application requires standard underwriting review.",
            risk_score=risk_score
        )

Step 3: Executing the Pipeline

Let’s test this architecture with a sample application.

if __name__ == "__main__":
    # Initialize components
    risk_model = RiskClassificationModel()
    decision_engine = LoanDecisionModel(risk_model)

    # Sample applicant payload
    applicant = LoanApplication(
        applicant_id="USR-99821",
        credit_score=720,
        debt_to_income=0.35,
        requested_amount=60000.0,
        has_bankruptcy_history=False
    )

    # Run evaluation
    result = decision_engine.evaluate(applicant)
    
    print(f"--- LOAN EVALUATION REPORT ---")
    print(f"Applicant ID: {applicant.applicant_id}")
    print(f"Decision Status: {result.status}")
    print(f"Computed Risk Score: {result.risk_score:.4f}")
    print(f"Audit Trail Reason: {result.reason}")

When you run this code, every single rejection or approval has a crystal-clear audit trail. If an auditor asks why a loan was denied, you can point directly to line 55 or line 61 of the decision engine. You aren’t guessing at feature weights; you are executing verifiable business logic.


When to Choose Which: The Engineering Matrix

To make this actionable for your next system design review, keep this architectural matrix handy:

Metric ML Classification Decision Models
Primary Driver Pattern recognition in unstructured/complex data Explicit business rules and compliance policies
Explainability Low (requires SHAP/LIME, often black-box) High (fully auditable, deterministic trace)
Maintenance Cost High (retraining, data drift monitoring) Low (code updates, direct rule modifications)
Handling Edge Cases Poor (hallucinates or misclassifies out-of-distribution data) Excellent (explicit fallback rules handle edge cases cleanly)
Latency Medium to High (heavy matrix math/transformer inference) Ultra-low (O(1) lookups or light boolean evaluations)

Conclusion: Stop Chasing Hype

Machine learning classification is one of the most powerful tools ever invented by computer science, but using it to replace basic conditional logic is like using a laser-guided missile to swat a fly.

The next time you are tasked with building a classification or decision-making system, take a step back. Ask yourself: Is this a perception problem requiring generalization, or is this a policy problem requiring governance?

Build hybrid pipelines. Keep your deterministic logic explicit, and let machine learning handle the probabilistic heavy lifting where it actually belongs. Your auditors, your DevOps team, and your sanity will thank you.

Recommended

Level up on this topic

Hand-picked resources I'd point a colleague to.

See my full toolkit →
AI assistants

ChatGPT

OpenAI's assistant for writing, coding, analysis and brainstorming.

Why I recommend itMy go-to for quick drafts and talking through a modelling idea out loud.

Try ChatGPTon chatgpt.com · Not sponsored

Some links are affiliate links: if you buy through them I may earn a small commission, at no extra cost to you. I only recommend things I've used or would recommend to my own team. Details

Search Data Science Chronicles

Tip: press / or ⌘K anywhere to search.