Blog

  • Is CI/CD Your Savior? Think Again.

    A pragmatic look at the hidden costs, the small-batch myth, and why user trust matters more than deploy frequency.


    I. Introduction

    “CI/CD will save you money. It will make your team faster. It will eliminate bugs. It is the only way to build modern software.”

    We’ve all heard this. The industry preaches CI/CD as a universal solution. Every conference talk, every DevOps blog post, every vendor pitch tells you the same thing: adopt CI/CD or be left behind.

    But is it really that simple?

    After years of watching teams adopt (and struggle with) CI/CD, I’ve come to a different conclusion. CI/CD is a powerful tool—but it is not a magic bullet. When implemented poorly, it can cost more money than it saves, introduce more bugs than it prevents, and damage user trust faster than any traditional release process ever could.

    This post is not an anti-CI/CD rant. It is a call for thoughtful, context-aware implementation. I want to walk you through the real costs, the hidden traps, and the practical framework I’ve arrived at after questioning the orthodoxy.

    Let’s start with the most uncomfortable question of all.


    II. The Cost Analysis: Does CI/CD Actually Save Money?

    The Common Sales Pitch

    The CI/CD sales pitch is seductive:

    • “Automate everything and save developer hours.”
    • “Deploy faster and reduce operational costs.”
    • “Catch bugs earlier and reduce downtime.”

    It sounds logical. Who wouldn’t want faster deployments, fewer bugs, and lower costs?

    The Reality

    Here’s what the sales pitch doesn’t tell you:

    • CI/CD requires infrastructure: runners, cloud compute, artifact storage.
    • CI/CD requires expertise: SREs, DevOps engineers, platform teams.
    • CI/CD requires maintenance: fixing broken pipelines, updating dependencies, managing flaky tests.
    • CI/CD requires context-switching: developers losing focus while waiting for pipelines to finish.

    These costs are real. And for many teams, they outweigh the benefits.

    The Math (For a Team Deploying Weekly)

    Let’s run the numbers for a typical team of 5 developers deploying once per week, running linters and unit tests 50 times per developer per day (a realistic number during active development).

    ActivityFrequencyManual TimeCI/CD TimeDifference
    Linting50x/week25 min25 minTie
    Unit Tests50x/week100 min100 minTie
    Deploy to Staging1x/week5 min1 minCI/CD saves 4 min
    Smoke Tests1x/week3 min2 minCI/CD saves 1 min
    Deploy to Production1x/week15 min1 minCI/CD saves 14 min
    Total148 min129 minCI/CD saves 19 min/week

    Monthly savings: ~76 minutes of human time. Less than an hour and a half.

    Monthly cost of CI/CD:

    • SRE/DevOps salary: ~$5,000/month
    • Cloud compute costs: ~$500/month
    • Developer context-switching from flaky tests: ~$500/month of lost productivity

    Total monthly cost: ~$6,000.

    Cost per minute saved: $6,000 / 76 minutes = **~$79 per minute saved.**

    That is a terrible ROI. For a team deploying once a week, CI/CD is a net loss.

    When CI/CD Actually Saves Money

    CI/CD only becomes financially viable when three conditions are met:

    1. High deployment frequency. If you’re deploying multiple times per day, the time savings compound rapidly. A team deploying 10 times per day saves ~3,800 minutes per month—a completely different equation.
    2. Large engineering teams. The cost of SREs and platform maintenance is diluted across more developers.
    3. Complex infrastructure. When manual deployment is risky (multiple services, complex dependencies, compliance requirements), the safety net of automation is worth the cost.

    The Startup Tax

    For startups, the equation is even worse. A CI/CD platform can triple infrastructure costs and add maintenance overhead before product-market fit is even found.

    Here’s what a startup typically does:

    “We set up GitLab CI with 4 different stages, parallel testing, and Kubernetes deployments before we even had our first paying customer.”

    Here’s what they actually needed:

    • A simple deploy script (rsync or scp to a single server).
    • A few well-written unit tests.
    • Time spent talking to users, not debugging pipelines.

    The result of the enterprise approach:

    • Cloud bill triples before they have revenue.
    • Developer velocity drops by 30-50% due to pipeline wait times.
    • The team feels “professional” because they have a complex pipeline—but they’re still building the wrong product.

    The “You Are Not Google” Rule

    This is the single most important rule for startups:

    “You are not Google. You do not have Google’s problems. Do not use Google’s solutions.”

    Google needs 10,000 deploys a day because they have billions of users and thousands of microservices. You have 10 users and a monolith. Your problems are fundamentally different.

    The Simple Alternative

    For a pre-product-market-fit startup, here’s all you need:

    bash

    #!/bin/bash
    # deploy.sh — The entire "CI/CD" pipeline for a startup
    
    echo "Building the app..."
    npm run build
    
    echo "Deploying to server..."
    rsync -avz --delete ./dist/ user@your-server.com:/var/www/app/
    
    echo "Restarting the server..."
    ssh user@your-server.com "pm2 restart app"
    
    echo "Done. Deployed in under 2 minutes."

    Total complexity: One file. 10 lines.

    Total maintenance burden: Zero.

    Total cost: $0 for CI/CD infrastructure.

    Total time spent on “DevOps”: Zero.

    The Rule: If your deploy process can be written in 10 lines of bash, do not use a CI/CD platform. The moment you introduce a YAML file, you introduce a new configuration language to learn, a new platform to debug, and a new tax on your engineering time. None of that is worth it before you have product-market fit.


    III. The “Small Batches = Low Risk” Myth

    The DevOps Mantra

    “Deploy small, frequent changes to reduce risk.”

    This is one of the most widely repeated claims in DevOps. It sounds logical: smaller changes are easier to test, easier to review, and easier to roll back. Therefore, small batches must be safer.

    The Reality

    Small batches do not eliminate risk. They shift risk from a single, obvious failure to a slow accumulation of complexity and emergent problems.

    Emergent Behavior: The Blind Men and the Elephant

    Imagine 10 developers each making one tiny, perfectly safe change to a car:

    • Developer 1 adjusts the fuel injector (small, safe).
    • Developer 2 tweaks the ignition timing (small, safe).
    • Developer 3 recalibrates the brake pressure (small, safe).
    • Developer 4 updates the tire pressure sensor (small, safe).

    Each change is independently reviewed, tested, and deployed. No bugs. Low risk.

    Now, you turn the car on.

    The engine knocks, the brakes lock up, and the tire sensor reads “flat” because the fuel injector, ignition timing, and brake pressure all interact in ways none of the individual developers anticipated.

    This is emergent behavior—problems that only arise when independent, “safe” parts are combined. You cannot test for this with unit tests or even integration tests that run in isolation. You only discover it in the integrated, running system.

    Real-World Examples of Accumulated Failure

    Database Schema Drift
    10 small deployments add 10 new columns to the database. Each is fine alone. But performance degrades slowly over 2 months. One day, a query that used to take 200ms now takes 12 seconds. Production crashes. No single deploy caused it. The accumulation did.

    Cascading Dependencies
    Service A changes a response field (deploy 1). Service B starts using it (deploy 2). Service C relies on Service B’s old response format (deploy 3). All three deploys were tested independently. None of them broke. But when all three are live, Service C crashes because it’s reading data that no longer exists.

    Hidden Technical Debt
    Each small deploy is “good enough” and “low risk.” But 200 small deploys later, the codebase is a spaghetti mess of hacky fixes. The “big unforeseen problem” is that the team now takes 3x longer to add any new feature because the code is unmaintainable.

    What Actually Reduces Risk?

    If small batches alone don’t reduce risk, what does?

    1. Observability, Not Just Testing. You cannot test for emergent behavior. You can only observe it. Elite teams invest heavily in monitoring, logging, and distributed tracing to detect emergent issues before they affect all users.
    2. Canary Deployments. Roll out changes to 1% of users first. Watch metrics. If anything looks wrong, roll back instantly.
    3. Feature Flags with Kill Switches. The ability to instantly turn off a feature, without rolling back the entire deployment.
    4. Chaos Engineering. Proactively injecting failures into your system to see how it behaves under stress. This is how you discover that your 10 small deploys have made your system fragile.
    5. Regular “Hardening” Sprints. Even elite teams don’t just deploy constantly without reflection. They have periods where they step back, consolidate, refactor, and address the accumulated technical debt that the 200 small deploys created.

    The Takeaway

    Small batches are a tool, not a silver bullet. They allow you to move faster and recover quicker. But they do not eliminate the need for deep architectural thinking, robust observability, and a healthy respect for the fact that the whole is always greater and more dangerous than the sum of its parts.


    IV. The User Trust Problem: Deployment ≠ Release

    The Critical Distinction

    This is one of the most misunderstood concepts in modern software delivery:

    • Deployment: Moving code to production servers. A behind-the-scenes technical event.
    • Release: Making a feature available to users. A user-facing event.

    Many teams conflate these two concepts. They assume that because they can deploy, they should release to everyone.

    The result: users see half-baked, buggy, or confusing features and lose trust.

    The “Feature Fatigue” Trap

    When a team adopts CI/CD and trunk-based development, they often fall into a dangerous mindset:

    “We can deploy anytime, so we should deploy everything as soon as it’s ‘code complete.’”

    This creates a user experience that feels:

    • Unstable: The UI keeps changing. Buttons move. Workflows shift. The user can’t build muscle memory.
    • Unpolished: Features are released with rough edges, missing error states, or poor performance because the team rushed to “get it out.”
    • Untrustworthy: Users learn to dread updates because they associate them with broken functionality.

    The cost: Users don’t complain. They just leave. And they don’t come back.

    How Elite Teams Protect User Trust

    The best teams use a set of tools and strategies to ensure that users never see a feature until it is polished, tested, and ready.

    1. Feature Flags (Progressive Rollout)

    This is the primary tool. Here’s how it protects user trust:

    • Deploy the feature to production, but keep it flagged “off” for everyone except internal testers.
    • Test it internally for days or weeks.
    • Turn the flag on for 1% of real users. Monitor metrics aggressively.
    • Slowly increase to 5%, then 20%, then 50%, then 100%.
    • At any sign of trouble, turn it off instantly.

    Result: By the time a feature reaches 100% of users, it has been running in production for days or weeks, under real traffic, and has been proven stable. The user sees a polished, working feature.

    2. Canary Releases

    Gradually roll out changes to a small percentage of users to monitor for issues before a full launch.

    3. Beta Programs

    Explicitly label new features as “Beta” or “Early Access.” Users who opt in know they might be rough. They are volunteers, not victims.

    4. Dark Launches

    Deploy code but keep it completely hidden from users. Use it to test performance and gather metrics without user impact.

    The Cost of Showing Too Early

    ScenarioUser PerceptionBusiness Impact
    Feature shown early, buggy, then fixed in 2 days“This app is unreliable. They keep breaking things.”10% of users churn. Support tickets spike. Brand damage.
    Feature shown early, polished, and works perfectly“Nice! This app keeps getting better.”User retention improves. Word-of-mouth referrals increase.

    The difference between these outcomes is not the code. It is the release strategy.

    The Takeaway

    Every time you show a user something new, you are asking them to trust you. If that thing is broken, confusing, or unpolished, you are spending that trust. If it happens too often, you go bankrupt.

    Deploy constantly. Release carefully. Never let a user see a feature until it is polished and proven.


    V. The Dirty Secret: The Code Graveyard

    Unreleased, Abandoned Code

    Fast deploys + feature flags + aggressive experimentation = a massive amount of code that never reaches users.

    How Unreleased Code Accumulates

    Abandoned Features
    A developer builds a new feature, wraps it in a flag, deploys it, and tests it with 1% of users. The data shows users don’t like it. Product decides to pivot. The flag stays “off” forever. The code sits in production, unused, for years.

    Perpetual Beta
    A feature is built, flagged as “Beta,” and released to 5% of users. Feedback is mixed. The product team keeps tweaking it. Months pass. The feature never reaches 100%. It lingers in a permanent “Beta” state until it’s eventually deprecated.

    A/B Test Losers
    Two versions of a feature are built (Variant A and Variant B). Both are deployed behind separate flags. Variant A wins the A/B test. Variant B is turned off and abandoned. 50% of the development effort is completely wasted.

    Strategic Pivots
    The company changes strategic direction. An entire initiative—maybe 6 months of work—is no longer relevant. The code is deployed, flagged off, and eventually deleted. Hundreds of thousands of dollars of engineering time, vaporized.

    The Numbers

    This is not theoretical. The industry has studied this extensively.

    • Microsoft Research found that 40-60% of features built by software teams are never used or are used so little that they don’t justify the development cost.
    • Standish Group’s Chaos Report found that 45% of features in a typical software project are never used.
    • In the world of A/B testing and feature flags, it’s widely accepted that 50-80% of new features fail to improve the key metric they were designed to improve.

    For Companies Without Abundant Budgets, This Is Catastrophic

    The industry tells you: “Fail fast! Experiment! Deploy everything!”

    What they don’t tell you:

    • Experiments cost money. Real money.
    • The data is often noisy, incomplete, or misleading.
    • A/B tests and feature flags are cheap to set up but expensive to build the code for.
    • Dead code accumulates silently and is rarely cleaned up.

    For a well-funded unicorn, 40% waste is an acceptable cost of experimentation. For a startup or a mid-sized company, that 40% waste can be the difference between profitability and bankruptcy.

    A Real-World Budget Scenario

    Let’s imagine a 10-person engineering team with an annual burn rate of $1.5 million.

    ScenarioApproachOutcome
    Traditional (No CI/CD)Build 10 features. Plan carefully. Release 8. 2 fail.20% waste. $300,000 wasted.
    Fast Deploy (Unthoughtful)Build 10 features. Deploy them all with flags. 6 fail. 4 succeed.60% waste. $900,000 wasted.

    The difference: $600,000 down the drain. For a startup, that’s runway. For a mid-sized company, that’s a team laid off. For an enterprise, that’s a budget overrun that kills other initiatives.

    The True Cost of Dead Code

    Cost TypeDescriptionFinancial Impact
    Development CostSalaries paid to build features that never ship.Direct, measurable. $10,000–$100,000+ wasted per abandoned feature.
    Maintenance CostDead code still needs to be compiled, tested, and deployed. It slows down pipelines.Indirect. Longer pipelines = developer wait-time = lower productivity.
    Cognitive LoadDevelopers must navigate through dead code, abandoned flags, and unused codepaths.Hard to measure but significant. 10-20% productivity drag.
    Technical DebtDead code interacts with live code in unexpected ways. It creates bugs.Emergency fixes, outages, support tickets. All cost money.
    Opportunity CostEngineering time spent on abandoned features could have been spent on features that actually deliver value.The biggest cost of all.

    How to Avoid the Code Graveyard (On a Budget)

    1. Validate Before You Build
    Don’t write a single line of code until you’ve validated the need with users. Use prototypes, mockups, and user interviews. Cost: A few hours. Risk: Zero.

    2. Kill Features Early
    The earlier you kill a feature, the less it costs.

    • Kill it on a whiteboard: $0 wasted.
    • Kill it in a prototype: $1,000 wasted.
    • Kill it in an MVP: $10,000 wasted.
    • Kill it after full development: $100,000+ wasted.

    3. Set a “Feature Expiration Date”
    Every feature flag gets a deadline. If the feature hasn’t reached 100% rollout by that date, it gets deleted. This prevents features from languishing in “perpetual Beta” purgatory.

    4. Track the “Experiment Cost” Metric
    Measure how much engineering time is spent on features that are ultimately abandoned. If the ratio exceeds 20-30%, you’re experimenting too aggressively. Adjust your planning process accordingly.

    5. Dedicate Regular Cleanup Sprints
    Every quarter, spend 1-2 days cleaning up dead code. Delete permanently-off feature flags. Remove unused code paths. Refactor where possible.

    The Bottom Line

    For companies without abundant budgets, unthoughtful fast deployment is a financial trap.

    • The industry tells you: “Deploy everything. The data will guide you.”
    • The reality is: “Every abandoned feature is thousands of dollars of wasted engineering time.”

    The smart approach:

    • Experiment cheaply (prototypes, user interviews, mockups).
    • Build expensively (only after validation).
    • Clean up aggressively (regular code graveyard maintenance).

    VI. The Contradiction at the Heart of CI/CD

    The Developer’s Dilemma: Time Is a Zero-Sum Game

    Every developer has a fixed number of hours in a day. Every hour spent writing tests is an hour not spent building features. Every hour spent debugging a flaky pipeline is an hour not spent delivering value.

    When you ask a developer to:

    • Write the code
    • Write the unit tests
    • Write the integration tests
    • Write the E2E tests
    • Maintain the test suite
    • Debug the pipeline when tests fail

    …you are asking them to do two jobs in the time they used to do one.

    The result: Developers cut corners. They write the minimum viable tests. They stop expanding prematurely. They do exactly what you observed—they “spare” operations because they are measured on shipping features, not on test coverage.

    This is not laziness. This is a rational response to an impossible workload.

    The Tester’s Contradiction: Automation vs. Human Judgment

    Now consider the other side. If you add human testers to catch what developers miss, you introduce a fundamental tension:

    The CI/CD pipeline is automated. Human testers are manual. The two do not naturally fit together.

    Here is the contradiction:

    The Automation IdealThe Reality with Human Testers
    “Code is merged. Tests run automatically. Deployment happens instantly.”“Code is merged. Automated tests run. But wait—we need to wait for QA to do manual testing. The pipeline pauses.”
    “Every change is deployed within minutes.”“Every change is deployed within minutes—after QA gives the green light, which takes hours or days.”
    “No human intervention. Everything is scripted.”“Human intervention is required. The pipeline must stop and wait.”

    The moment you add human testers, the “continuous” in CI/CD breaks. The pipeline is no longer fully automated. It now has a manual gate. And manual gates are slow, inconsistent, and expensive.

    The Awkward Compromise

    Most teams try to resolve this tension with an awkward hybrid:

    1. Automated tests run first. They catch the low-hanging fruit.
    2. If they pass, the pipeline stops and waits. A human tester is notified.
    3. The human tester does exploratory testing. They find edge cases.
    4. If they approve, the deployment proceeds. If they find bugs, the pipeline fails and the developer gets notified.

    This “works” in the sense that it functions. But it has severe drawbacks:

    • The pipeline is no longer continuous. It is now “continuous until a human says yes.”
    • The human tester becomes a bottleneck. Every deployment must wait for them.
    • The team is stuck in the worst of both worlds. They have the complexity of CI/CD and the slowness of manual QA.

    The Real Cost: Adding Testers Doesn’t Fix the Problem—It Just Moves It

    ConfigurationDeveloper TestingHuman TestersPipeline SpeedBug RateCost
    No CI/CD, Manual TestingDevelopers write minimal testsHuman testers catch bugsVery slow (deployments take days)LowModerate (QA salaries)
    CI/CD, No TestersDevelopers write minimal testsNo one catches what developers missVery fastHighModerate (infrastructure)
    CI/CD + Human TestersDevelopers write minimal testsHuman testers catch bugsStops and waitsModerateHigh (infrastructure + QA salaries)

    The worst of all worlds: CI/CD + Human Testers. You pay for the infrastructure, you pay for the testers, and you still have a slow release process.

    The Takeaway

    “CI/CD does not eliminate the need for testers. It just makes the trade-off between speed and quality more visible—and more painful.”

    The industry has created an impossible standard: “We will deploy 10 times a day, with zero bugs, and no testers.” That standard is not realistic. It is aspirational marketing masquerading as engineering best practice.


    VII. The Infinite Regress of Test Automation

    The “Test Engineer” Solution

    When I point out that developers are “spare-ers” and testers are “cover-ers,” someone inevitably says:

    “The solution is obvious. Hire test engineers. They write automated tests. The tests run in CI/CD. Problem solved.”

    It sounds logical. Test engineers are trained to think like cover-ers. They write test code. The test code runs automatically. The pipeline stays fast and automated. No manual gate. No human tester bottleneck. Perfect.

    But this solution contains a fatal flaw.

    The Role Inversion Problem

    Test engineers write test programs. The test programs are now the “product.” The original application—the thing the testers used to test—is now just the “system under test.”

    Here is the problem: Test engineers face exactly the same constraints as developers.

    RoleProductConstraintResult
    DeveloperApplication codeLimited hours, pressure to ship featuresSpares operations. Writes minimal tests.
    Test EngineerTest codeLimited hours, pressure to ship test suitesSpares operations. Writes minimal test coverage.

    The role is inverted, but the problem is identical.

    • The test engineer has a fixed number of hours.
    • They are measured on how much test coverage they produce.
    • They must prioritize which test cases to automate.
    • They face pressure to ship the test suite, just like developers face pressure to ship features.
    • They are human. They make mistakes. Their test code has bugs.

    Test engineers are not cover-ers anymore. They are spare-ers who happen to write test code.

    The Infinite Regress

    Now consider the implications:

    1. Test engineers write test programs. They think like cover-ers, but they operate under spare-er constraints. They cut corners. They miss edge cases. Their test suites are incomplete.
    2. Who tests the test programs? Test programs are code. They can have bugs. They can be incomplete. They can fail to catch the edge cases they were designed to catch.
    3. Option A: Developers test the test programs. But developers are spare-ers. They will write minimal tests for the test programs. The same problem recurs.
    4. Option B: Test engineers test each other’s test programs. But now you have an infinite loop—test engineers testing test engineers’ tests. At some point, someone must use human judgment to decide that the tests are “good enough.”
    5. Option C: The test program tests itself. But that is a circular dependency. A test program cannot validate its own completeness without a separate mechanism.

    The result: You have created a new category of code that must be maintained, debugged, and updated. You have added complexity, not eliminated it. You have not solved the “cover-er vs. spare-er” problem. You have just pushed it up one level.

    The Cost of the Test Engineer Solution

    CostDescriptionBusiness Impact
    Development CostTest engineers must be hired. They write and maintain test code.Adds headcount. Adds salary expenses.
    Maintenance CostTest code must be updated when the application changes. The suite grows over time.Test suites become brittle. Flaky tests appear. Pipeline slows down.
    Debugging CostWhen a test fails, someone must debug it. Is it a real bug? A flaky test? A test bug?Developer time wasted on false positives.
    Coverage CostTest engineers inevitably prioritize. Some edge cases are never automated.The coverage gap remains. The most obscure bugs still escape.
    Opportunity CostTime spent writing and maintaining test code is time not spent building the product.Slower feature delivery. Less time for innovation.

    Why the Test Engineer Solution Fails in Practice

    Let me illustrate with a concrete example.

    Scenario: You have a web application. You hire a test engineer. They write automated E2E tests using Selenium.

    What happens:

    1. The test engineer writes test cases for the critical paths: login, search, checkout.
    2. They run in CI/CD. The pipeline passes. Everyone celebrates.
    3. Two months later, the UI changes. A button moves. The test breaks.
    4. The test engineer spends hours fixing the test. They are frustrated. This is not fun work.
    5. Over time, the test suite becomes brittle. Tests fail randomly. Developers ignore the failures.
    6. The test engineer leaves. No one knows how to maintain the tests. The suite is abandoned.
    7. The team is back where they started, but now they have a broken test suite and no budget to fix it.

    The lesson: Test engineers are not a magic bullet. They face the same constraints as developers. They are subject to the same pressures. Their test code is just as flawed as any other code.

    The “Testing the Test Program” Problem

    Now consider an even more subtle issue.

    The test engineer writes a test to verify that the application’s login works correctly. The test passes. How do we know the test is correct?

    • Maybe the test only checks that the HTTP status is 200, but the login actually failed silently.
    • Maybe the test checks the wrong element, so it passes even when the UI is broken.
    • Maybe the test passes locally but fails in CI due to environment differences.

    How do you test the test program? The answer is: You don’t. You rely on human judgment. You look at the test and say, “That seems right.”

    This is the “cover-er” mindset again. But the test engineer—who is now a spare-er under pressure—will naturally stop expanding prematurely. They will assume the test is correct. They will not probe for edge cases in their own test code.

    The problem is not a lack of technical skill. The problem is the economic and cognitive constraints that apply to anyone who writes code, regardless of their job title.

    The Real Solution: Testers, Not Test Engineers

    The solution is not to replace testers with test engineers. The solution is to keep testers as cover-ers, use automation to amplify them, and accept that some manual testing will always be necessary.

    RoleJob DescriptionTools
    Tester (Cover-er)Find bugs through exploratory testing, edge case discovery, and user-centric testing.Manual testing, exploratory testing, usability testing.
    Automation EngineerWrite automation to handle repetitive tasks, freeing up testers.CI/CD pipelines, automated regression tests, performance tests.
    Developer (Spare-er)Build the system, write unit tests, and maintain the codebase.Unit tests, integration tests, code reviews.

    The key: The tester stays a cover-er. They are not required to write code. They are required to think like a user, probe the system, and find bugs.

    The automation engineer writes the code that handles the boring stuff. But they do not replace the tester. They complement them.

    The Takeaway

    “If you make testers write code, you turn cover-ers into spare-ers. You haven’t solved the problem. You’ve just moved it.”

    The infinite regress is real. At some point, you must rely on human judgment. There is no way to automate judgment itself.


    VIII. The Real Solution: Milestones + Small Batches

    Why Milestones Alone Fail

    The traditional milestone-only approach has well-known problems:

    • Too slow. Too risky. No feedback until the end.
    • Assumes you know everything upfront. You don’t.
    • Integration hell at the end. Crunch time. Burnout.
    • You only discover that users hate the feature after 3 months of work.

    Why Small Batches Alone Fail

    As we’ve discussed, the small-batch-only approach also has critical flaws:

    • No strategic direction. You’re moving fast, but possibly in circles.
    • Emergent complexity accumulates. Technical debt builds.
    • No time for big refactors because you’re always shipping.
    • The codebase rots slowly. Eventually, velocity grinds to a halt.

    The Winning Combination

    The most effective teams use a dual-track approach:

    • Milestones define the “What” and “Why” (Strategy): Quarterly outcomes. Business goals. User-facing value.
    • Small batches define the “How” and “When” (Execution): Small, deployable units that move toward the milestone.

    The Process

    Step 1: Talk to Users Before You Code
    Validate the need. Understand the problem. Don’t write a single line of code until you’ve confirmed that someone actually wants what you’re building.

    Step 2: Prototype Before You Build
    Show users low-fidelity mockups. Iterate on design. Cost: A few days. Risk: Zero.

    Step 3: Build a “Concierge” or “Wizard of Oz” MVP
    Don’t build the full system. Do the work manually behind the scenes. If users love it, then build the automation.

    Step 4: Build Small and Release Carefully
    Use feature flags, canaries, and beta programs. Never let a user see a feature until it is polished and proven.

    Step 5: Consolidate Regularly
    Hardening sprints to refactor, improve tests, and address technical debt. This is not optional. It is essential to prevent the accumulation of technical debt and dead code.

    The Cost-Savings Math

    PracticeCostBenefit
    User Validation (Pre-Development)A few hours of interviewsSaves 40-60% of development budget
    Prototyping (Pre-Development)A few days of design workSaves months of rework
    MVP (Small Build)2-3 weeks of developmentTests the market cheaply
    Feature Flag Cleanup (Quarterly)1-2 days of engineering timePrevents long-term productivity drag
    Hardening Sprints (Quarterly)1-2 weeks of engineering timePrevents technical debt accumulation

    Total cost of disciplined approach: ~10-15% of engineering budget.

    Total cost of unthoughtful fast deployment: 40-60% of engineering budget wasted on dead code.

    Savings: 25-45% of your engineering budget.

    A Real-World Example

    TimeframeMilestone (Strategic)Small Batches (Execution)
    Month 1“Support Apple Pay.”Deploy 1: Add SDK. Deploy 2: Update API. Deploy 3: Add UI. Deploy 4: Enable flag for internal testers. Deploy 5: Fix bug. Deploy 6: Canary to 5% of users. Deploy 7: Roll out to 100%.
    Month 2“Improve checkout speed by 30%.”Deploy 8: Optimize database query. Deploy 9: Add caching layer. Deploy 10: Compress assets. Deploy 11: Profile and tune.
    Month 3Hardening SprintNo new features. Only refactoring, test improvements, documentation, and dependency updates.

    IX. The Startup Playbook

    The Pre-Product-Market-Fit Phase

    Before you have product-market fit, your only job is to find something people want. Everything else is a distraction.

    PhaseRecommended ApproachWhy
    Pre-Product-Market-FitA simple deploy script (rsync, scp, or a basic PaaS like Heroku/Railway). Minimal CI (just linting and unit tests).You need to move fast and change direction quickly. Complex infrastructure locks you into decisions you haven’t validated yet.
    Post-Product-Market-FitGradual introduction of CI/CD. Start small (build, test, deploy). Add complexity only when the pain of manual processes exceeds the cost of automation.You now have paying customers. Stability matters. But still, be conservative.
    ScalingFull CI/CD pipeline with feature flags, canary releases, and multiple environments.You have the revenue and team to support it.

    When to Actually Introduce CI/CD

    Not when you think you need it. Not when a blog post tells you to.

    Only when all of these conditions are met:

    1. You have product-market fit. Users are paying, retention is good, growth is happening.
    2. Manual deployments are painful. You’re deploying multiple times a day, or the process is error-prone.
    3. You have a dedicated engineer or team. Someone owns the pipeline and maintains it.
    4. You have budget to spare. The cost of the platform + the maintenance time is negligible compared to revenue.

    Until then, rsync and scp are your best friends.


    X. Conclusion: The Pragmatic Path

    The Key Takeaways

    1. CI/CD is not a universal cost-saver.
    It only pays off at scale—high deployment frequency, large teams, complex infrastructure. For a team deploying once a week, it’s a net loss.

    2. Small batches do not equal low risk.
    They shift risk to emergent complexity and accumulated technical debt. The whole is always greater and more dangerous than the sum of its parts.

    3. Deployment ≠ Release.
    Deploy constantly. Release carefully. Protect user trust above all else. Never let a user see a feature until it is polished and proven.

    4. The code graveyard is a luxury only the rich can afford.
    For companies without abundant budgets, unthoughtful fast deployment is a financial trap. 40-60% waste is catastrophic, not strategic.

    5. CI/CD creates a fundamental contradiction.
    Automation and human judgment do not naturally fit together. Adding testers breaks the “continuous” in CI/CD. Removing testers allows bugs to reach users.

    6. Test engineers are not the solution.
    If you make testers write code, you turn cover-ers into spare-ers. The infinite regress is real. Test code has bugs. Test code requires maintenance. Test engineers face the same constraints as developers.

    7. The real solution is milestones + small batches + human testers.
    Strategy guides execution. Execution enables strategy. And human judgment—the “cover-er” mindset—cannot be automated away.

    8. For startups, simple is better.
    A 10-line bash script beats a 200-line YAML pipeline every time. Don’t adopt enterprise tooling before you have product-market fit.

    The Final Verdict

    CI/CD is a tool, not a religion. It is not your savior. It will not fix poor planning, insufficient customer communication, or a lack of product discipline.

    More importantly, CI/CD does not eliminate the need for human testers. It does not eliminate the need for the “cover-er” mindset. It does not automate judgment, curiosity, or the ability to probe for edge cases.

    • If you have no testers, you will ship more bugs. Developers are spare-ers. They will always prioritize features over tests.
    • If you force testers to write code, you turn them into spare-ers. The infinite regress is real. Test code has bugs. Test code requires maintenance. Test engineers face the same constraints as developers.
    • If you keep testers as cover-ers and use automation to amplify them, you get the best of both worlds. But it costs money and requires a culture shift.

    The pragmatic path:

    1. Start simple. Use a deploy script or a basic PaaS.
    2. Validate your product. Talk to users. Find product-market fit.
    3. Keep human testers as cover-ers. Their judgment is irreplaceable.
    4. Use automation for the repetitive, predictable tasks.
    5. Add CI/CD incrementally. Only when the pain of manual work exceeds the cost of automation.
    6. Accept that perfection is impossible. Some bugs will ship. Monitor, rollback, and learn.

    Think before you deploy. Plan before you code. Listen before you build. And never forget that judgment cannot be automated.


    XI. Call to Action

    What has your experience been with CI/CD? Has it saved you money or cost you more than you expected? Have you fallen into the code graveyard trap? Have you struggled with the tester contradiction?

    Share your story in the comments. Let’s learn from each other’s mistakes and successes.

    If you found this post valuable, subscribe for more pragmatic takes on software development.


    This post was inspired by a long and thoughtful discussion with a skeptical engineer who asked all the right questions. Thank you for challenging the orthodoxy.

  • It’s Time to Rethink Agile. The Albatross Method Is the Advanced PM’s Choice.

    Why project managers who have outgrown reactive rituals are turning to proactive, component-based engineering.


    Introduction: A Global Crisis Beneath the Agile Banner

    Software projects are still failing at alarming rates—despite decades of “Agile adoption.” Engineers are burning out in London, Bangalore, New York, and Tokyo. Morale is collapsing. Quality is declining. The methodologies we were told would save us—Scrum and Kanban—are often making things worse.

    Agile promised adaptability, but delivered rigid rituals. Agile promised collaboration, but delivered status meetings. Agile promised quality, but delivered technical debt.

    The problem is not Agile’s values. The problem is the way Agile has been packaged, sold, and dogmatized around the world. It is time to rethink Agile—not abandon it, but evolve beyond it.

    The answer is Albatross: a proactive, component-based methodology for serious projects and advanced PMs—anywhere on the planet.

    “Software modules are not some vague ideas. They cannot fit some fixed sprints well.”


    The Problem with Scrum: A Ritual Disguised as Agility

    Why Sprints Are Breaking Engineers and Projects Worldwide

    Scrum, in theory, is about empiricism and continuous improvement. In practice, it has become a system of relentless pressure, surveillance, and performative busyness—from San Francisco to Singapore.

    The “Dark Scrum” Phenomenon:

    • Velocity as a performance metric: The treadmill of ever-increasing points. If a team delivers 30 points in a sprint, management expects 31 the next sprint. Engineers are forced to overestimate or kill themselves to hit a number designed to go up forever.
    • Stand-ups as status reports: The daily 15-minute sync has morphed into a status-report torture session. Engineers spend their first hour stressed about what they will say, rather than entering a flow state.
    • The delivery obsession: True Agile values “working software over comprehensive documentation.” Dark Scrum values ticket closure over working software. Engineers ship half-baked features to close sprints, accruing massive technical debt.
    • Retrospectives as gaslighting: Teams give honest feedback. Leadership ignores it. Nothing changes. This creates learned helplessness—a primary driver of depression.

    The Core Flaw:

    Software is a system of logical, interconnected modules. Forcing these modules into fixed 2-week sprints creates “dependency hell.” Engineers spend more time fitting work into sprints than solving problems.

    The Moral Injury:

    Engineers know they are building on shaky foundations. They are forced to prioritize speed over quality. This creates deep professional distress—whether in Austin, Amsterdam, or Auckland.

    “Scrum is not breaking software engineers. Broken management hiding behind Scrum is breaking software engineers.”


    The Problem with Kanban: Flow Without Direction

    Why Visualizing Work Is Not the Same as Visualizing Dependencies

    Kanban, at its best, is a powerful system for managing flow. At its worst, it is a reactive system that blinds you to the critical path—anywhere it is deployed without critical thinking.

    What Kanban Does Well:

    • Pull-based workflow.
    • WIP limits to prevent overload.
    • Continuous delivery.

    What Kanban Cannot Do:

    • Visualize dependencies between components.
    • Identify the critical path before bottlenecks occur.
    • Provide a committed delivery date.
    • Give stakeholders a clear roadmap.

    The Reactivity Problem:

    Kanban shows you where the bottleneck is (cards pile up in a column). It does not show you where the bottleneck will be in two weeks. By the time you see the jam, the delay is already baked in.

    Consider a dependency graph:

    text

    Component A (Database) → Component B (API) → Component D (UI)
                                                ↘
    Component C (Authentication) → Component E (Integration) → Component F (Deployment)

    In this graph, the critical path is A → B → D → F. If A is delayed by 2 weeks, F is delayed by 2 weeks—regardless of how fast C and E are completed.

    Kanban completely fails to visualize this. It shows a flat board with columns like “To Do,” “In Progress,” “Review,” “Done.” It does not show you that A is a prerequisite for B, or that B is a prerequisite for D.

    The Toyota Paradox:

    Toyota uses Kanban for manufacturing (where the design is fixed). Toyota uses Gantt-like charts for product development (where dependencies matter). Software development is product development, not manufacturing.

    “Kanban is a crutch for uncertainty. But if your components are small enough to estimate accurately, you do not need a crutch.”


    The Albatross Method: A Proactive Alternative for the World

    Navigating Complex Projects with Clarity and Precision

    The Symbolism of the Albatross:

    • High-flying, far-seeing.
    • Guides sailors across vast oceans.
    • Navigates by reading winds and currents.
    • Symbol of good fortune and successful journeys.

    The albatross does not ask for permission to fly. It reads the winds, plans its course, and navigates with precision. That is what Albatross does for software projects—in any country, on any continent.

    The Core Principles of Albatross

    1. Architecture First

    • Build a UI prototype to match the product owner’s imagination.
    • Create an “overlook design” showing how the system is organized.
    • Establish agreement on data flow and component boundaries.
    • Validate designs with users before coding begins.

    Why This Works Globally: Whether your culture favors direct communication or consensus-building, a visual prototype creates a shared understanding that transcends language and cultural differences.

    “We do not start building until the business has done its job. The business’s job is to know what it wants. If it cannot articulate that, no methodology will save the project.”

    2. Component Decomposition

    • Break the system into a DAG (Directed Acyclic Graph) of small, logical components.
    • Each component is small enough to estimate accurately (in hours, not story points).
    • Dependencies between components are explicit and documented.

    Why This Works Globally: Component decomposition is a universal engineering practice. It works for distributed teams, co-located teams, and everything in between.

    3. Critical Path Visualization

    • Map all components and dependencies onto a living Gantt chart.
    • Identify the critical path early.
    • Tackle critical components first with the best resources.
    • Update the Gantt chart based on feedback.

    Why This Works Globally: Every project, everywhere, has dependencies. Visualizing the critical path is a universal project management need—whether your stakeholders expect firm dates or flexible forecasts.

    4. Continuous Feedback Integration

    • Validate each component with the product owner as it is completed.
    • Use feedback to adjust the remaining Gantt chart.
    • No arbitrary sprints—feedback happens at natural boundaries.

    Why This Works Globally: Feedback at natural boundaries—not arbitrary sprints—is universally more efficient. It works across time zones, cultures, and organizational structures.

    5. Proactive Communication

    • Developers follow the Gantt chart for clear task assignment.
    • Faster developers help slower ones.
    • Stuck developers ask for help (or managers intervene).
    • No member friction. No blame culture.

    Why This Works Globally: Clear task assignments, mutual support, and a no-blame culture are universally beneficial—whether your team is hierarchical or flat.


    Comparing Albatross to Scrum and Kanban

    AspectScrumKanbanAlbatross
    Planning ApproachFixed sprintsPull-based flowProactive Gantt + DAG
    Dependency VisualizationNoneNone (until jammed)Explicit (critical path)
    EstimationStory points (vague)Optional (cycle time)Component hours (accurate)
    Feedback TimingEvery 2 weeks (arbitrary)ContinuousAt component boundaries (natural)
    PredictabilityLow (velocity fluctuates)Moderate (forecast only)High (committed delivery date)
    Stakeholder TrustLow (constant changes)Low (no firm dates)High (clear roadmap)
    Developer Mental HealthPoor (burnout)ModerateHigh (clear expectations)
    Adaptability to ChangeHigh (but chaotic)High (but reactive)Moderate (with Gantt updates)
    Cultural AssumptionHigh autonomy, flat hierarchyContinuous flow, predictable demandFunctional organization, any culture

    The Key Insight:

    Albatross is not “Waterfall.” It is architecture-centric, component-based, feedback-integrated engineering. It assumes a functional organization—where product owners know their domain and developers are trusted to estimate accurately. In that environment, it is superior to both Scrum and Kanban—in Tokyo, in Texas, and everywhere in between.

    “Scrum and Kanban are tools for managing failure—failure to plan, failure to communicate, failure to get clarity. Albatross assumes a functional organization. It is engineering, not ceremony.”


    Addressing Common Objections

    Objection 1: “Albatross is just Waterfall with a Gantt chart.”

    Response: Waterfall does all design upfront and delivers at the end. Albatross validates the UI and architecture upfront, then delivers components incrementally with continuous feedback. This is Evolutionary Delivery, not Waterfall.

    Objection 2: “What if requirements change weekly?”

    Response: If requirements change weekly, the product owner does not know what they want. The solution is not to adopt Kanban—it is to stop coding and force clarity upfront through UI prototypes, user testimony, and design agreements. This is exactly what Albatross does.

    Objection 3: “Albatross is too rigid for startups.”

    Response: Albatross is designed for complex, enterprise systems with fixed deadlines, regulatory requirements, and external dependencies. For startups in uncharted territory, a lighter approach may be appropriate. Albatross is for engineering, not experimentation.

    Objection 4: “Gantt charts are outdated.”

    Response: Gantt charts are only outdated when used as a whip. In Albatross, the Gantt chart is a communication and coordination tool—it visualizes dependencies, identifies the critical path, and helps developers see where to focus. It is a roadmap, not a straitjacket.

    Objection 5: “You’re just rejecting Agile.”

    Response: We are not rejecting Agile values—we are rejecting the ritualistic, dogmatic implementations that have replaced those values. Agile is about individuals, working software, and customer collaboration. Albatross delivers all three better than Scrum or Kanban.

    “If your Gantt chart is obsolete every week, the problem is not the Gantt chart. The problem is that your stakeholders are not doing their homework.”


    Why Methodological Colonialism Hurts Everyone

    The Global Problem of Copying Without Thinking

    Around the world, IT industries suffer from an inferiority complex toward Silicon Valley. Companies import methodologies (Scrum, Kanban, SAFe) without understanding the underlying principles—or whether they even fit the local context.

    This happens everywhere:

    • In Europe, enterprises adopt US frameworks to appear “modern,” even when their regulatory environments demand meticulous upfront planning.
    • In Asia, companies copy US rituals without adapting them to local work cultures.
    • In Latin America and Africa, startups import Silicon Valley playbooks that assume venture capital funding and high turnover, neither of which apply locally.
    • Even in the US itself, many companies have adopted Scrum so dogmatically that they have lost sight of the original Agile values.

    The result is global:

    • Theater, not results.
    • Burnout, not productivity.
    • Technical debt, not quality.
    • Cynicism, not engagement.

    “The religious belief that ‘US = best’ is not just a problem in one country. It is a global problem. And it is time to rethink it everywhere.”


    Why Albatross Fits Anywhere

    Universal Principles for a Global Audience

    Albatross is not a “Japanese method” or an “American method.” It is an engineering-first method whose principles are universal:

    1. Architecture First Works Everywhere

    Whether you are in Tokyo, Berlin, or San Francisco, building a UI prototype and an overlook design before coding reduces rework everywhere. The method works regardless of whether your culture favors consensus or individualism. It simply asks for clear communication before commitment.

    2. Component Decomposition Is Universal

    Breaking a system into a DAG of small, estimable components is a universal engineering practice. Whether your team is co-located or distributed, the DAG provides a shared mental model that transcends language and time zones.

    3. Critical Path Visualization Transcends Culture

    Every project, everywhere, has dependencies. Visualizing the critical path is a universal project management need. Whether your stakeholders expect firm dates (Germany, Japan) or flexible forecasts (US startups), the Gantt chart provides clarity.

    4. Continuous Feedback Is Culturally Neutral

    Feedback at natural boundaries—not arbitrary sprints—is universally more efficient. Whether your culture values direct feedback (US, Israel) or indirect feedback (Japan, many Asian countries), component validation creates a structured, neutral space for it.

    5. Proactive Communication Benefits All Teams

    Clear task assignments, mutual support, and a no-blame culture are universally beneficial. Whether your team is hierarchical or flat, the method provides a framework for healthy collaboration.

    “Albatross is not a Japanese method. It is an engineering method that happens to work well in Japan—and everywhere else.”


    A Case Study: Japan’s Experience Is a Warning to the World

    How Real Japanese Companies Work—and Why It Fails

    Japan’s IT industry is not a niche case. It is a canary in the coal mine for what happens when an entire industry imports methodologies without critical thought. The problems Japanese companies face are the same problems emerging in enterprises worldwide—just magnified by cultural and structural factors.

    The Reality on the Ground:

    • Agile adoption is growing, but the picture is mixed. According to a 2025 survey of 400 system engineers, 23.2% of companies have adopted Agile development, while 50.0% still use Waterfall, and 26.8% use a hybrid approach . The PMI Japan Chapter’s 2025 survey found that 44.8% of organizations have adopted Agile, recovering to previous levels, though the momentum for further expansion has weakened .
    • However, “strict” Agile is rare. An analysis of IPA data reveals that approximately 70% of companies that claim to use Agile are actually using “non-strict” Agile—often referred to as “fake Agile” or “Scrum-but.” Only 20% use strict Agile . This means many companies have adopted the ceremonies of Agile (stand-ups, sprints, retros) without the underlying principles.
    • The multi-layered subcontracting structure (多重下請け構造) remains. Large projects are still passed down a pyramid: prime contractor → 1st-tier subcontractor → 2nd-tier → 3rd-tier → often offshore. About 70% of software firms engage in re-subcontracting . This creates a “telephone game” where the original business intent is lost by the time it reaches the developers actually writing code.
    • Legacy systems are a massive burden. According to IPA’s 2024 survey, 60.6% of financial sector companies still have legacy systems, and overall, 53.3% of user companies hold legacy systems . Over 63% of these legacy systems remain on-premise .
    • Development and operations are separated. In typical Japanese IT departments, development and operations are run by entirely separate teams, often from different vendors. This makes it difficult to foster shared understanding of business goals and team culture. Team members limit their goals to “fulfilling their assigned tasks,” which crushes motivation.
    • Engineers are evaluated on quantity, not quality. One consultant who investigated a major Japanese IT firm observed: “Japan’s system development methods kill capable engineers. Management is excessive, and personnel evaluation criteria are based on the amount of code or documents written. If you obediently churn out volume as instructed, you are evaluated positively. But if you try to change something to improve quality, you are scolded—or ignored.”

    The Problems They Encounter

    1. “Fake Agile” and Ritual without Substance

    Because strict Agile is rare, many companies practice “Scrum theater”—they hold stand-ups and sprints, but the underlying culture has not changed. The NPS (Net Promoter Score) among Agile-experienced PMs in Japan is -20, meaning more practitioners are dissatisfied than satisfied . Only 7% prefer to “always use Agile,” while 82% prefer “case-by-case” adoption . This suggests widespread disillusionment.

    2. Scope Creep with No Control

    Dysfunctional clients make unreasonable demands, and dysfunctional managers accept them. The scope of work grows endlessly. “Even if the client and manager are decent, there may be omissions in the initial plan. When that happens, you cannot suddenly increase headcount. The capable members of the project team end up bearing the burden.”

    3. The “Telephone Game” Requirement Distortion

    Because of the multi-layered subcontracting structure, “the client’s needs and issues are summarized and reinterpreted at each level. By the time they reach the engineers at the bottom, they have drifted from the original intent.” The result: “A system that works according to the specification, but no one can explain why this feature is necessary.”

    4. The “Killing Capable Engineers” Syndrome

    Capable engineers—especially younger ones—suffer the most. They see the inefficiency, try to improve it, and get punished. “The more capable the engineer, the more cognitive dissonance they experience. Their opportunities for creativity are stolen, and their spirit is crushed.” One consultant observed: “The ones with bright eyes, the younger talent, are the ones who break down first.”

    5. Documentation and Planning Challenges

    Among Agile teams, 37.5% report difficulty creating overall release plans and roadmaps, and 25.5% cite skills gaps as a challenge . Even when Agile is adopted, teams struggle with the planning and scaling that complex systems require.

    How Albatross Helps Them Avoid/Solve These Problems

    Problem 1: “Fake Agile” and Ritual without Substance

    How Albatross Helps: Albatross replaces arbitrary sprints and ceremonies with component-based delivery. There is no “Scrum theater”—teams deliver real components at natural boundaries. Feedback is integrated, not performed in a 2-week sprint review where nothing changes.

    Problem 2: Scope Creep with No Control

    How Albatross Helps: The Component DAG + Gantt Chart makes scope changes visible. A new feature is not just “added”—it becomes a new component with its own dependencies and impact on the critical path. The product owner can see exactly how adding a feature shifts the delivery date. This is transparent, not adversarial.

    Problem 3: The “Telephone Game” Requirement Distortion

    How Albatross Helps: The UI Prototype First step forces the product owner (not a middle manager) to visually validate the system before coding begins. This breaks the “telephone game” by creating a shared visual reference that survives subcontracting layers. When a 3rd-tier offshore developer sees a UI prototype, they know what to build—no matter how many layers of interpretation preceded it.

    Problem 4: “Killing Capable Engineers”

    How Albatross Helps: The Help, Not Blame culture built into Albatross ensures that fast developers help slow ones, and stuck developers ask for help without shame. Evaluation is based on component completion and quality, not “lines of code written.” This removes the perverse incentive to churn out mediocre work. The Gantt chart is a neutral tool, not a weapon for micromanagement.

    Problem 5: Planning and Roadmap Challenges

    How Albatross Helps: The Critical Path Visualization addresses the top challenge Agile teams face—difficulty creating release plans and roadmaps. The Gantt chart provides exactly the visibility that 37.5% of Japanese Agile teams are missing . The DAG shows dependencies, and the timeline shows the delivery date.

    Problem 6: Legacy System Complexity

    How Albatross Helps: For the 53.3% of companies still burdened by legacy systems , Albatross’s component-based approach allows systematic modernization. Each component can be a bounded unit of modernization, with clear interfaces to legacy systems. The DAG makes the migration path visible, and the Gantt chart tracks progress.

    Problem 7: Responsibility Is Unclear

    How Albatross Helps: The Component DAG assigns clear ownership to each component. When a component fails, the responsible team is obvious. When a dependency is delayed, the Gantt chart shows exactly who needs to help whom. Transparency eliminates the blame-shifting game that plagues multi-layered subcontracting structures.

    “Japan’s experience with Agile is a warning to the world. The same problems exist in every country—they are just hidden better. Albatross is not another ritual. It is a return to engineering.”


    Practical Implementation: How to Start Using Albatross

    A Step-by-Step Guide for Advanced PMs

    Step 1: Validate the Vision

    • Build a UI prototype (not production code).
    • Show it to users and collect testimony.
    • Get formal sign-off from the product owner.

    Step 2: Create the Overlook Design

    • Map the system architecture.
    • Define components and their boundaries.
    • Agree on data flow and interfaces.

    Step 3: Decompose into a DAG

    • Break the system into small, estimable components.
    • Identify dependencies between components.
    • Document the DAG visually.

    Step 4: Estimate Each Component

    • Estimate in actual hours (not story points).
    • Use engineering judgment, not guesswork.
    • Add buffer for uncertainty (5%).

    Step 5: Build the Gantt Chart

    • Map components and dependencies onto a timeline.
    • Identify the critical path.
    • Allocate resources to critical components first.

    Step 6: Execute with Communication

    • Developers follow the Gantt chart for task assignment.
    • Faster developers help slower ones.
    • Stuck developers ask for help.
    • Validate each component with the product owner upon completion.
    • Adjust the Gantt chart based on feedback.

    Step 7: Review and Adjust

    • At component boundaries, review progress with stakeholders.
    • Update the Gantt chart as needed.
    • Communicate timeline changes transparently.

    Tool Recommendations:

    • Gantt chart: Microsoft Project, Smartsheet, or even Excel.
    • DAG visualization: Draw.io, Lucidchart, or Miro.
    • UI prototyping: Figma, Sketch, or Adobe XD.

    “The Gantt chart is a living document. It is updated based on real feedback, not abandoned because of chaos.”


    The Human Impact: Protecting Developer Mental Health

    Why Albatross Is Good for Engineers Everywhere

    The Burnout Epidemic (A Global Phenomenon):

    • Scrum’s velocity treadmill.
    • Kanban’s lack of direction.
    • Constant context switching.
    • Moral injury from building bad software.

    How Albatross Helps:

    • Clear expectations: Developers know what to work on next.
    • No arbitrary sprints: Work is delivered when it is actually done.
    • Component ownership: Developers own complete components, not fragmented stories.
    • Help, not blame: Faster developers help slower ones. Stuck developers ask for help.
    • Quality pride: Components are finished properly, not rushed to close a sprint.

    The Psychological Safety Advantage:

    • The Gantt chart is a neutral tool, not a weapon.
    • Being behind is a signal to ask for help, not a reason for shame.
    • Finishing early is a positive signal to help others, not a punishment (more stories).

    “The goal is not to go faster. The goal is to go well—with clarity, respect, and engineering integrity.”


    Conclusion: The World Needs Albatross

    Engineering Outlasts Fashion

    Scrum and Kanban are reactive, process-oriented, and often harmful—in every country. Albatross is proactive, architecture-centric, and engineer-respecting. It visualizes dependencies, identifies the critical path, and delivers predictable results.

    Call to Action for Project Managers Everywhere:

    • Stop copying methodologies without deep thinking.
    • Adopt Albatross for complex, serious projects.
    • Trust your engineers. Trust your planning. Trust your communication.

    The religious belief in Scrum is a global fad. Engineering is not a fad. Albatross is engineering. And engineering always outlasts fashion—in Japan, in the US, in Europe, and everywhere else.

    “The albatross does not ask for permission to fly. It reads the winds, plans its course, and navigates the vast ocean with precision and grace. That is what Albatross does for software projects—anywhere in the world.”


    Frequently Asked Questions

    Q: Is Albatross suitable for small teams?
    A: Yes. The method scales down well—the key is the component decomposition and dependency visualization, which are valuable for any team size.

    Q: What if my organization requires Scrum?
    A: You can adopt Albatross principles within a Scrum framework. Use component-based estimation instead of story points. Use a Gantt chart for dependency visualization alongside the sprint backlog.

    Q: How do I convince stakeholders to adopt Albatross?
    A: Start with a pilot project. Show them the Gantt chart and critical path. Demonstrate how it provides clarity and predictability. Let the results speak for themselves.

    Q: Is Albatross only for Japan?
    A: No. Albatross is a global methodology. Its principles are universal. Japan is simply a particularly instructive case study because it amplifies the global problem of methodological colonialism.

    Q: What if my team is distributed across time zones?
    A: Albatross works well for distributed teams because the DAG and Gantt chart provide a shared, asynchronous visual reference that transcends time zones.


    References

    1. PMI Japan Chapter, “Agile Research Group Survey,” 2025.
    2. Lychee Redmine, “Survey of 400 System Engineers,” 2025.
    3. IPA (Information-technology Promotion Agency), “Software Trends Survey,” 2024.
    4. IPA, “Software Development Data Survey,” 2023.
    5. Japan Fair Trade Commission, “Survey on Software Subcontracting,” 2022.

    Recommended Reading

    • The Mythical Man-Month by Frederick Brooks
    • Lean Software Development by Mary and Tom Poppendieck
    • The Goal by Eliyahu Goldratt (for understanding bottlenecks and critical path)
    • Toyota Production System (for understanding pull-based flow and its limits)
    • Rational Unified Process (for component-based architecture)

    This post is intended for project managers, technical leads, and engineering executives around the world who are ready to move beyond reactive rituals and embrace proactive, engineering-driven project delivery.

  • Introducing MoniServ: Your Lightweight, Vigilant Service Monitor

    In the world of system administration and development, keeping your services online and healthy is paramount. Manually checking if a web server is responding or a background process is still running is tedious and error-prone. That’s where MoniServ comes in—a simple, powerful, and highly convenient tool designed to take the worry out of service monitoring.

    MoniServ is a no-nonsense, Rust-written utility that excels at monitoring your services and performing simple, automated recoveries. Its brilliance lies in its blend of built-in functionality and limitless extensibility.

    MoniServ is available on: https://github.com/Afante/monitor-services

    Here’s why MoniServ is such a convenient tool for your toolkit:

    1. Effortless Setup and Configuration
    Getting started with MoniServ is refreshingly straightforward. There’s no complex installation procedure—just build the single executable file and copy it to where you need it. Configuration is equally painless, using simple, human-readable YAML files stored in a dedicated directory. This file-based approach makes it easy to version, back up, and manage your monitoring setups. The included sample configuration file is heavily commented, guiding you through every setting so you can have your first monitor running in minutes.

    2. Two Powerful Monitoring Modes in One
    MoniServ gives you flexibility without complexity:

    • Built-in Web Monitoring: For HTTP services, it’s ready to go. You can configure MoniServ to check a specific URL, using various HTTP methods (GET, POST, etc.), and it will intelligently verify the response by matching the status code or even the content of the reply using regular expressions. This provides deep, content-aware verification.
    • Custom Command Monitoring: For anything else, MoniServ’s power truly shines. By setting the kind to custom, you can run any script or command as your health check. This means you’re not limited to web services—you can monitor databases, local processes, disk space, or any other system state you can check with a script.

    3. Automated Recovery & Smart Alerting
    MoniServ isn’t just a passive observer; it’s an active defender.

    • Self-Healing: When a service fails, you can define a recovery_cmd. This allows MoniServ to automatically execute a script to restart the service, clear a cache, or perform any other remedial action—often resolving the issue before you even know it happened.
    • Email Notifications: You won’t be left in the dark. MoniServ can be configured to send detailed error reports via email, ensuring you’re promptly alerted to any persistent issues that require your attention.

    4. Simple Yet Robust & Extensible
    Written in Rust, MoniServ is fast, reliable, and memory-safe. It handles the essential details like connection and read timeouts to prevent it from hanging. And while it’s perfect out-of-the-box for most needs, its ability to execute any custom script means its potential use cases are virtually limitless. It grows with your needs.

    In essence, MoniServ is the ultimate “set and forget” tool. It handles the routine vigilance, automates first-response recoveries, and alerts you when intervention is needed—all from a single, easy-to-configure executable. Say goodbye to manual checks and hello to peace of mind with MoniServ.

  • 论霍雨浩的人格虚伪:当主角的双标成为叙事逻辑的裂缝

    《斗罗大陆Ⅱ绝世唐门》的主角霍雨浩,是一个为无数读者带来过热血与感动的角色。他的坚韧、智慧与对感情的执着,构成了其人格魅力的基石。然而,当我们将审视的目光从“主角光环”上移开,回归到统一的价值逻辑与人性的常理之中,便会发现霍雨浩的人格内核存在着一道深刻的裂缝——一种根植于“按需切换”的道德双标,以及由此衍生出的、难以被情节发展所完全消解的虚伪感。

    一、冰海之行:以武力相逼的闯入者

    霍雨浩虚伪的第一个典型场景,发生在他为提升灵眸武魂而深入冰海,寻找海公主一族的过程中。他闯入海公主一族世代栖息的领地,然而他并非海公主的客人,也未曾获得过对方的许可。更关键的是,海公主一族对人类有着极深的仇恨,因为她们曾遭受过人类的欺骗与伤害。霍雨浩明知这一背景,却依然强行深入,完全符合“强盗”的定义:未经允许进入他人领地,用暴力或威胁手段获取自己想要的东西。据动画观众反馈,他在冰海的表现是“嚣张狂妄”的,完全不像原著中那样对人鱼族客气,他的交涉态度并非平等的协商,而是以武力相逼的“恐吓”,最终演变为激烈的交战,并通过缔结魂灵契约,将海公主的女儿变成了他的第六魂灵。在此过程中,海公主一方本是受害者——她们被人类欺骗过、被侵犯过,如今又被人强行闯入领地。而霍雨浩,则扮演了“持械闯入、以武力逼迫主人就范”的入侵者角色。

    二、劫持太子事件:站在道德高地的批判者

    与冰海事件形成鲜明对比的,是霍雨浩在日月帝国太子徐云瀚被劫持事件中的表现。当时,为了阻止日月帝国的侵略战争,三大帝国派出十五位封号斗罗,成功劫持了年仅一岁多的徐云瀚作为人质。然而,霍雨浩得知此事后,却怒斥实施劫持的天阳斗罗是“卑劣小人”,并强行将徐云瀚送回了日月帝国皇宫。他站在“正义”的立场上,指责对手绑架儿童是不择手段的卑鄙行径,认为战争不应牵扯无辜的孩子。

    三、魂师大赛:作弊者的“荣耀”辩护

    霍雨浩的双标还体现在他对“规则”的态度上。第一届魂师大赛期间,他使用精神共享能力帮助场上的队友进行战斗,明显属于场外协助,按照比赛规则属于作弊行为。然而霍雨浩对此毫无愧色,反而一副理所当然的样子,声称是为了“捍卫史莱克的荣耀”。到了第二届魂师大赛,当王秋儿代表史莱克学院在赛场上搏命时,霍雨浩竟在观众席上与她完成了武魂融合技,直接提供了战力支持。这种公然作弊的行为,在他口中依然被包装成“捍卫史莱克的荣耀”。当他自己违反规则时,总能找到高尚的理由——“为了荣耀”“情势所迫”;而当他指责他人时,却从不考虑对方是否也有“迫不得已”的苦衷。

    四、魂灵窃取:比“本分”的敌人更卑劣

    更为讽刺的是,日月帝国曾在拍卖会上花钱买下一个十万年魂兽胚胎,没有抢夺或暗杀,反而本本分分地付了钱。而霍雨浩却借助本体宗偷袭明德堂的混乱,把人家花钱买来的东西偷走了。如果日月帝国做得“本分”,那霍雨浩的行为又该作何评价?当他在道德高地上指责敌人的“不择手段”时,他的行为已经比他谴责的对象更加不堪。

    五、明都爆炸:恐怖主义的修辞包装

    霍雨浩“按需道德”的极致体现,是他在引爆明都地下军火库时的选择。他明知那里是人口密集的市区,却依然选择了引爆,导致明都三分之一被夷为平地,无数平民丧生。面对同伴的质疑,他的回应是“雪崩的时候,没有一片雪花是无辜的”。

    这句话将一个危险的逻辑推向了极端:在一个战争国家,每一个平民——哪怕是老人和孩子——都不再是“无辜”的,因为他们纳税、他们生活在敌国、他们没有反抗自己的统治者。如果我们把这个逻辑抽离出故事语境,它会呈现出非常典型的恐怖主义辩护模式:发动侵略的国家,其人民都可以被视为“帮凶”,所以攻击他们的城市是正当的;生活在敌国领土上的每一个生命,都不再享有“平民”的保护身份;为了最终的胜利,大规模杀伤平民可以被美化为“战略需要”。

    更具讽刺意味的是,在日月帝国发动侵略战争时,霍雨浩曾怒斥橘子的屠城命令,站在道德的制高点上指责她对平民施加暴力。而当他本人为了削弱日月帝国而引爆军火库、间接屠杀了大量平民时,他却用了“雪崩时没有一片雪花无辜”的逻辑来为自己开脱。这种对同一行为(杀伤平民)的双重标准——“你做就是残忍,我做就是不得已”——让他在道德上的虚伪感达到了顶峰。

    六、“按需道德”的虚伪本质与叙事逻辑的裂缝

    霍雨浩虚伪的根源在于:他的“道德标准”不是恒定不变的,而是随着自身需求的变化而“按需切换”的。当他处于弱势或需要占据道德高地时,他会强调“正义”“规则”“不伤害无辜”;当他需要突破规则来达成个人目标时,他又会拿出“形势所迫”“为了变强”“迫不得已”等理由来为自己开脱。这种“严于律人,宽以待己”的双重标准,构成了霍雨浩人格虚伪的核心。

    事件霍雨浩的角色他的行为他的道德判断
    劫持太子事件道德批判者指责对方绑架儿童是“卑劣”,强行送回人质“不择手段是可耻的”
    冰海事件闯入者与获益者未经许可闯入他人家园,以武力相逼,获取魂灵“形势所迫,这是必要的”
    魂师大赛作弊规则破坏者两届大赛均使用场外协助“为了捍卫史莱克的荣耀”
    明都爆炸恐怖行为实施者引爆军火库,屠杀平民“雪崩时没有一片雪花无辜”

    当一个主角的道德体系完全围绕自我需求旋转时,其人格的统一性便宣告瓦解。霍雨浩身上那种“我做就是迫不得已,别人做就是十恶不赦”的逻辑,使得他时而站在道德高地上批判他人,时而又用同样的“不择手段”去达成自己的目的。他早已从“正义的守护者”蜕变为一个“按需正义”的双标执行者,而作者试图用“成长”和“形势所迫”来为之辩护的叙事努力,反而暴露了故事本身在逻辑上的裂缝——一个无法用统一标准来衡量的主角,其“正义”便不再是正义,而只是主角光环的另一种修辞。

    ※本文是在经过与AI的讨论后,由AI总结生成的。

  • Why Predicting the Next Day Price of a Stock Using the Stock’s Price History Is Impossible, with a DNN.

    It can always be a fascinating dream to predict the next day price of a stock by looking at the stock’s price curve. However, by thinking it through, I doubt the meaningfulness of do so, even with a very powerful technology, deep neural network.

    A stock’s price, as time goes, rises or falls, from minute to minute. To view it abstractly, one can think of it can a continuous curve. A prediction task usually shifts a time window from past time toward current time. In each shift of the window, the oldest value is removed, and a newest value is added. In this process, one problem happens, that is the subsequence of any window is very similar to the subsequence of the next window. And next day’s price can either rise or fall, seemingly randomly. So, the input dataset is like assigning different labels to the same input data. Hence, the best that any model can do it to make a random guess on whether the next day’s price will rise or fall.

    Some people have made some videos on predicting stock price using LSTM, or other models. As long as they use the stock price history to predict price of the same stock, it will not work, just because the inputs do not distinguish labels.

    However, does it mean that stock prices cannot be predicted? Asserting this is still too early. There can be some relationships among various stocks, so there is still some possibility that one stock’s price can be predicted by other stocks.

  • Understand Relu as a Piecewise Function

    Relu is an activation function defined as:$$
    Relu(x)=\begin{cases}
    x, & x > 0 \\
    0, & otherwise
    \end{cases}
    $$. This function has an interesting property that it can act like an open/close switch. To see why, suppose $\vec{x}$ is a vector, its entries are $x_i$’s. When a Relu layer is applied to $\vec{x}$, it will be an entry-wise check. Only those $x_i$’s that are greater than $0$ are passed, just as if electricity passes through a closed switch. All non-positive values are zeroed out, just as if electricity is blocked by an open switch.

    When a neural network uses Relu layers inside it, it can be thought as each of the Relu layers is selecting some outputs of its preceding layer, therefore behaves as a feature selection. The training process of the neural network is then training a feature selection model. When the model has multiple layers, Relu is in fact selecting computed features.

    So, one can try to design a feature selecting process using Relu. Like $$\text{Input} \rightarrow \text{D} \rightarrow \text{Relu} \rightarrow \text{More DNN}$$. Here, the $\text{D}$ is a layer that is equivalent to multiplying a diagonal matrix. After several epochs of training, all entries in the input that correspond to non-positive entries in $\text{D}$ can be discarded.

  • Understand Why A Sole Linear Layer Does Not Classify Well

    In deep neural network, the most frequently used component may be the linear layer. However, the linear layer itself does not work well as a classifier. In this article, I intend to explain why from my own point of view.

    A linear layer is a linear function. Just as in the following form: $$F(A) = A (\vec{x})$$, where $A$ is a matrix, $\vec{x}$ is the input vector. The training process of a DNN is to find an optimal $A$ to maximize an objective function. In this process, $\frac{\partial{F}}{\partial{A}}$ is computed, and it is used to update $A$. To view $\frac{\partial{F}}{\partial{A}}$, we can rewrite $F$ as $$
    F'(A)=M vec(A)
    $$, where $vec(A)$ is the vectorized $A$, and $M$ is a corresponding matrix which contains suitably distributed entries from $\vec{x}$ so that $$
    F(A) = F'(vec(A)) = M vec(A)
    $$.
    Now, it is clear that $$
    \frac{\partial{F}}{\partial{A}} = \frac{\partial{F’}}{\partial{vec(A)}} = M
    $$. When an optimizer uses a multiple of $M$ to update $A$ as in a gradient descending algorithm, $A \leftarrow \vec{\Lambda} M$, where $\vec{\Lambda}$ is some vector that summarizes backpropagation of gradients from previous layers. Hence, it is clear that $A$ eventually only changes along some vector in the column space of $M$. When the optimal $A$ is not in the column space of $M$, no matter how $A$ is updated, the optimal point will never be reached.

    The above findings can help us improve design of even simple networks. For example, assume a layer $G(x) = A \vec{x}$, where $A$ is a $2 \times m$ matrix. When $\vec{x}$s are almost colinear, $A$ will only move along the $\vec{x}$s. Hence, it is quite difficult to achieve an effect like switching two entries of $A$.

    To solve the problem, the effective dimension of $\vec{x}$s must be increased, either directly by adding more various samples, or indirectly by expanding the model to multiple layers. For example, define $$
    F(\vec{x}) = A v( B \vec{x} )
    $$, where $v$ is some activation function. This is in fact two linear layers. $B \vec{x}$ first expands $\vec{x}$ into more various values, then $v$ introduces more linear-independency, before right-multiplying with $A$. With this result, Relu is clearly a good choice as an activation function, because it behaves far from a linear function, therefore, produces higher dimensions. Then, the high dimension makes columns of A combine with more variations.

    On the contrary, if $v$ is a sigmoid function, it changes smoothly everywhere, and behaves closely to a linear function at the origin. Hence, its ability of adding dimension is low, as compared to Relu.

    That is all! ……Wait! Not all!

    There is still a problem. The nonlinearity of Relu only occurs at the 0 point. But if the input to Relu is far from 0, then Relu will be virtually linear, hence lose the merits of Relu. This is where a good initialization needed. A good initialization will cover both + and – side of a Relu input for every input entry if the layer is large enough, hence will give enough dimensions for the column space of $B$. However, if $B$ is too large, it will be wasteful. A natural question is at least how large a layer must be. Support the input $\vec{x}$ has $p$ entries. Then to give chance to each of the entries to be passed and blocked by the Relu function, there must be at least two entries of $B$. So, there must be $2p$ entries in each row of B. Still, this does not guarantee that each $x$ is both passed and blocked in every row of $B$, because an initialization process is very likely a random process, it is only probable that the initialization goes perfectly. Hence, adding more than $2p$ entries can also be practical. Since then, $A$ can give enough combinations that can cover the whole space of $F$.

    In addition, to avoid overfitting, dropout has been the de facto method. If the dropout ratio is $d$, then $B$ must have at least $2p/d$ entries.

    Conclusion: A least practical DNN need have the form $F(\vec{x}) = A \circ B \circ \vec{x}$, $B$ should be at least double number of columns of $\vec{x}$.

  • バイブコーディングのブームがそろそろ過ぎ去ろうとしているのではないか

    動画サイトでは、まだまだバイブコーディングの話題で盛り上げています。しかし、すでにいくつかの楽観視できない結果でデータとして出ています。最近は下記の結果が目に飛び込んできました。

    The AI Productivity Boom that Wasn’t | L&DeepDive

    要約しますと、ベテランプログラマーであれば、生成AIを使って、20%早くなったという錯覚が生じて、実際は20%遅くなったという結果になった。

    これでは、バイブコーディングのきれいな泡はほぼ弾けてしまいました。

    バイブコーディングでは、もともとテクにある負債を神速に積み上げることが予見されています。今回の結果を加えて、生産性を落としていることも再度確認されました。

    もちろん、生成AIは一つの発展方向ですが、実際のビジネス環境というすごく複雑な文脈(コンテキスト)をどうやって一つの生成AIに低コストに詰め込むことは相当難しいでしょう。

    Claude Code社では、80%-90%のコードはAIに書かせていると宣言したことですが、あれば、自社製品作成の一環としてもみなすことができ、通常のプロジェクト開発と比べて、コスト上昇に対する容認度はかなり高いでしょう。

    バイブコーディングはプロダクションコードの主力になるまでまだまだ時間がかかりそうですね。

  • I made a browser extension that helps generate low-code test cases.

    Are you a pure programming lover? Or you are someone who dares not to challenge programming?

    I’d prefer to choose the latter as testers. The reason is that the people who are not programmers tend to focus more on how users would look at the system. The problem is then how to let them make automated tests.

    Letting testers do even low code tests could be a bit overwhelming. The only real solution seems to record and replay. But most of the tools just record to Python or JS codes. And I do not want source codes presented in faces of testers.

    How to solve this dilemma?

    My answer is a self-made browser plugin! I call it the CommandRecorder.
    The CommandRecorder simply records user actions and replay them in order. The codes that are generated are in Japanese! Accompanied is a test runner which is hidden behind a GUI program. The testers just need to select which case file and which sheet to run.

    The test case books are in Excel file format. An Excel file has some good features that a plain text file cannot provide.
    E.g.,
    – Excel sheets can use powerful formulae.
    – Cells are well aligned.
    – To repeat a pattern, you just need to drag.
    – Data areas by purpose can be cleanly separated by sheets.
    – Editing are straight forward, you don’t have to know structuring grammars.

    The only difficulty of using Excel files is there lacks an effective comparison tool for finding version differences. But what the hell! I just need to find time to make one.

    And more, have you noticed the output of the CommandRecorder is in Japanese? Yes, a natural language! This makes the resulting code very easily understandable. Even one who is completely unaware of the system and unaware of any programming language can understand what a test does. This largely flattens the learning curve.

  • Rust練習問題:数の整除

    問題文

    定理:二桁以上の正整数であれば、その一の位を取り除いて、残った数を前記一の位の数字の五倍で割ったら、残りの数が17の倍数の場合かつその場合に限って、元の数も17の倍数である。

    例えば、34が17の場合である。なぜなら、3-20=-17が17の倍数である。201は17の倍数ではない。なぜなら、20-5=15は17の倍数ではない。一個正整数nを入力して、あなたの任務はこれが17の倍数であるかを判断することだ。

    入力フォーマット

    入力ファイルは最多で10セットのテストデータを有する。一セットのテストデータは一行を占める。その行は一個の整数n(1<=n<=10^100)のみがあり、判断待ちの正整数を表す。n=0であれば、終了を意味し、この行を処理すべきではない。

    出力フォーマット

    一セットのテストデータに対して一行を出力して、相応のnが17の倍数であるかを表す。1が肯定を表し、0が否定を表す。

    入力サンプル

    34
    201
    2098765413
    1717171717171717171717171717171717171717171717171718
    0

    出力サンプル

    1
    0
    1
    0

    分析

    この問題の難点は、nの範囲にある。64-bitの整数型を使っても、表示範囲が最大で20桁の十進数くらいになる。100桁まで昇るnとしても使えない。つまり、プログラミング言語のビルトイン型では、nを表現できない。したがって、nを表現できる型またはそれに類するものを作らないといけない。

    また、前記定理によると、nが17の倍数であるかは、nより一桁小さい別の整数で判定できる。

    つまり、$n=n_0$が17の倍数という問題が下記の問題に相当する。
    ・$n_1$が17の倍数であるか、さらに次の問題に相当する。
    ・$n_2$が17の倍数であるか、さらに次の問題に相当する。
    ・$n_3$が17の倍数であるか、さらに次の問題に相当する。
    ・……
    そして、$n_1 > 10 n_2 > 10^2 n_3 > 10^3 n_4 > ……$

    すると、ある$n_p$がプログラミング言語のビルトイン型で表現できる大きさになったら、通常の割り算で17の倍数であるかは判定できるようになる。

    前記の定理の中に、掛け算と引き算がある。掛け算に参加する数字は小さく、引き算に参加する数字が大きい。このため、大きい数字の引き算を実装する必要があることになる。

    回答案

    use std::io;
    
    fn main() {
        let mut line = String::new();
        while io::stdin().read_line(&mut line).unwrap_or(0) > 0 {
            line = String::from(line.trim());
            if line == "0" {
                break;
            }
            let bn = BigNumber { digits: line.clone() };
            println!("{}", if is_divisible_by_17(&bn) { 1 } else { 0 });
            line.clear()
        }
    }
    
    struct BigNumber {
        digits: String
    }
    
    impl BigNumber {
        fn is_negative(&self) -> bool {
            if let Some(first_char) = self.digits.chars().next() {
                match first_char {
                    '-' => true,
                    _ => false
                }
            }
            else {
                false
            }
        }
    
        fn subtract(&self, other: &BigNumber) -> BigNumber {
            let mut result_digits = String::new();
            let mut ards : Vec<i8> = Vec::new();
            let mut brds : Vec<i8> = Vec::new();
            for c in self.digits.chars().rev() {
                match c {
                    '0'..='9' => ards.push((c as u8 - '0' as u8) as i8),
                    _ => panic!("Only unsigned numbers are allowed in minuend")
                }
            }
            for c in other.digits.chars().rev() {
                match c {
                    '0'..='9' => brds.push((c as u8 - '0' as u8) as i8),
                    _ => panic!("Only unsigned numbers are allowed in subtrahend")
                }
            }
            let mut diffds : Vec<i8> = Vec::new();
            let mut carry : i8 = 0;
            let maxlen = if ards.len() > brds.len() {ards.len()} else {brds.len()};
            for di in 0..maxlen {
                let mut df = 
                    if di >= ards.len() {0} else {ards[di]}
                    - if di >= brds.len() {0} else {brds[di]}
                    - carry;
                if df < 0 {
                    carry = 1;
                    df += 10;
                }
                else {
                    carry = 0;
                }
                diffds.push(df);
            }
            if carry == 1 {
                // The result is negative, and need be complemented.
                for d in diffds.iter_mut() {
                    *d = 9 - *d + carry;
                    if *d < 10 {
                        carry = 0;
                    }
                    else {
                        *d -= 10;
                    }
                }
                result_digits.push('-');
            }
            for d in diffds.iter().rev() {
                result_digits.push(('0' as u8 + *d as u8) as char);
            }
            BigNumber {
                digits: result_digits
            }
        }
    
        fn last_digit(&self) -> Option<i8> {
            if let Some(c) = self.digits.chars().rev().next() {
                Some((c as u32 - '0' as u32) as i8)
            }
            else {
                None
            }
        }
    
        fn remove_last_digit(&mut self) {
            if self.is_negative() {
                if self.digits.len() > 2 {
                    self.digits.pop();
                }
                else {
                    self.digits.clear();
                    self.digits.push('0');
                }
            }
            else {
                if self.digits.len() > 1 {
                    self.digits.pop();
                }
                else {
                    self.digits.clear();
                    self.digits.push('0');
                }
            }
        }
    }
    
    fn is_divisible_by_17(number: &BigNumber) -> bool {
        if number.digits.len() < 4 {
            let intval = number.digits.parse::<i32>().unwrap();
            return intval % 17 == 0;
        }
        let last_digit_times_5: u32 = number.last_digit().unwrap() as u32 * 5;
        let mut shortened = BigNumber {digits: number.digits.clone()};
        shortened.remove_last_digit();
        let subtrahend = BigNumber {digits: last_digit_times_5.to_string()};
        let difference = shortened.subtract(&subtrahend);
        return is_divisible_by_17(&difference);
    }