Thursday, August 27, 2026

I Delivered the Automation. The Final Test Changed Everything


 Freelance engineering projects rarely fail because of code alone.

Sometimes the architecture works. The deployment works. The integrations work. The milestone is delivered.

And then the final testing requirement changes.

This is a real-world lesson from a recent automation project involving Twilio, IVR automation, DTMF, NestJS, Next.js, PostgreSQL, Docker, and cloud deployment.

The experience was mixed.

Technically, it was a successful delivery.

Professionally, it reinforced why engineers must maintain clear boundaries around scope, testing, authorization, security, and ethics.

The Original Engineering Challenge

The project required building an automated IVR testing platform.

The core architecture involved:

  • Twilio outbound voice calls
  • Automated IVR interaction
  • Configurable DTMF timing
  • Webhook processing
  • Call-status tracking
  • PostgreSQL persistence
  • Dashboard visibility
  • Docker deployment
  • Cloud VPS infrastructure
  • Logging and error handling

The agreed Milestone 1 objective was essentially:

Outbound call → IVR interaction → DTMF input → webhook/status events → PostgreSQL persistence → logs

The implementation stack included:

Next.js Dashboard
        ↓
NestJS API
        ↓
PostgreSQL
        ↓
Twilio Voice API
        ↓
IVR
        ↓
Voice + Status Webhooks

Simple on paper.

More interesting in production.


Challenge 1: Timing Is Part of the Architecture

One of the biggest technical challenges was that IVR automation is not just about sending DTMF digits.

Timing matters.

The IVR needed:

Call connects
    ↓
Wait for IVR initialization
    ↓
Wait for the required menu stage
    ↓
Send DTMF
    ↓
Listen for the response
    ↓
Process webhooks

The timing configuration was made explicit:

  • IVR settle delay
  • Pre-DTMF wait
  • Delay between values
  • Post-DTMF listening window

This was important because hardcoding timing assumptions makes IVR automation fragile.

Instead, the system was designed around configurable timing parameters.


Challenge 2: Webhooks Must Be Treated as Unreliable Distributed Events

A major engineering consideration was webhook handling.

A call may generate:

  • voice callbacks
  • ringing events
  • answered events
  • in-progress events
  • completed events
  • duplicate events
  • events arriving in unexpected order

Therefore, the state machine had to prevent invalid transitions.

For example:

PENDING
  ↓
ASSIGNED
  ↓
CALLING
  ↓
WAITING
  ↓
DTMF_SENT
  ↓
SUCCESS

A completed webhook should not accidentally cause an already-successful attempt to be processed again.

Likewise:

DTMF_SENT

should not regress back to:

CALLING

This required forward-only state handling and idempotent webhook processing.


Challenge 3: Persistence Is More Important Than UI State

The dashboard should display the system state.

It should not be the system state.

The important data was persisted in PostgreSQL:

  • test runs
  • attempts
  • Twilio Call SID
  • webhook events
  • timestamps
  • statuses
  • results
  • errors

That meant the system could be inspected and validated independently of the frontend.

A successful automation system needs an audit trail.

For external integrations, logs alone are not enough.


Challenge 4: Deployment Problems Are Real Engineering Work

The application was deployed to a client-provided Ubuntu VPS.

The deployment included:

  • SSH access configuration
  • Docker
  • Docker Compose
  • PostgreSQL
  • NestJS production build
  • Next.js application
  • environment configuration
  • service health checks

The dashboard was eventually exposed directly through:

http://SERVER-IP/

rather than requiring:

http://SERVER-IP:3000

The deployment also required verifying:

Backend → Healthy
PostgreSQL → Healthy
Twilio Configuration → Valid
Webhook Configuration → Ready
Phone Line → Available

This became an important lesson:

Deployment is not the final step after engineering. Deployment is part of engineering.

Challenge 5: HTTPS Requires the Right Infrastructure

Twilio webhooks need publicly reachable HTTPS endpoints.

A bare server IP address creates a practical limitation.

A trusted TLS certificate generally requires a domain name under appropriate DNS control.

The deployment therefore required separating:

  • application hosting
  • public routing
  • webhook exposure
  • TLS certificates
  • DNS

For development and testing, a secure HTTPS tunnel can be useful.

For production, a domain with:

DNS
  ↓
Nginx / Reverse Proxy
  ↓
Let's Encrypt TLS
  ↓
Application API

is a cleaner architecture.


The Validation Strategy

Before attempting a live integration test, I created a preflight process.

The preflight checked:

  • API health
  • PostgreSQL connectivity
  • Twilio credentials
  • webhook configuration
  • timing configuration
  • phone-line availability

The result was:

PREFLIGHT PASS

Additional verification included:

  • API unit tests
  • production build
  • dashboard type checking
  • Docker health checks
  • PostgreSQL readiness

The principle was straightforward:

Do not spend money or consume external resources testing a live integration until everything that can be validated locally has already passed.

The Mixed Part of the Experience

This is where the project became a professional case study rather than just another deployment.

The original milestone was delivered and deployed.

The system was ready for the agreed validation flow.

Later, additional requirements were introduced around sequential testing and persistent progress across multiple calls.

Requirement changes are normal.

They happen in real projects.

The important issue is how those changes are handled.

A new requirement can affect:

  • architecture
  • database design
  • state management
  • scheduling
  • retry behavior
  • persistence
  • acceptance criteria
  • milestone scope

The key lesson:

A clarification is not always a clarification. Sometimes it is a new feature expressed as a testing requirement.

When Automation Needs a Boundary

Automation engineers solve difficult problems.

But not every technically possible automation should be implemented.

In this project, the final testing discussion moved toward functionality that I was not comfortable executing against a real financial system.

At that point, the correct engineering decision was not to optimize the algorithm.

The correct decision was to stop and reassess the environment and authorization boundary.

A responsible automation engineer should ask:

  • Is this a genuine sandbox?
  • Is the data synthetic?
  • Is the target explicitly designed for automated testing?
  • Is the requested behavior authorized?
  • Does the automation create security or fraud risk?
  • Is the acceptance criteria materially different from the agreed scope?

If the answers are unclear, pause.

That is not a failure to deliver.

That is professional engineering judgment.


The Biggest Lesson: Scope Control Is a Technical Skill

We often think of scope management as project management.

It is also an engineering discipline.

Consider the difference:

Initial requirement

One test call
→ One configured DTMF value
→ Webhook events
→ Database persistence
→ Logs

Expanded requirement

Persistent sequence
→ Multiple values
→ Multiple calls
→ Resume from next untested value
→ Prevent duplicate attempts
→ Detect success
→ Stop all workers
→ Save winning result
→ Recover after restart

Those are not equivalent systems.

The second introduces:

  • schedulers
  • persistent cursors
  • concurrency control
  • restart recovery
  • transactional state transitions
  • duplicate prevention
  • global stop conditions

That is a different level of system complexity.


What I Delivered

The delivered Milestone 1 system included:

  • Twilio integration
  • Automated outbound call initiation
  • IVR timing configuration
  • DTMF automation
  • Voice webhooks
  • Status webhooks
  • PostgreSQL persistence
  • Call event logging
  • State handling
  • Error handling
  • Docker deployment
  • VPS deployment
  • Production build validation
  • Health checks
  • Preflight validation
  • E2E validation procedure

The application was deployed on the client-provided infrastructure and prepared for authorized environment testing.


What I Would Do Differently Next Time

1. Freeze acceptance criteria earlier

Before coding:

Milestone
Scope
Inputs
Outputs
Test environment
Acceptance criteria
Out-of-scope items

should all be documented.

2. Separate sandbox credentials from production credentials

The environment should explicitly state:

SANDBOX ONLY

and use dedicated synthetic data.

3. Require test-environment details before milestone acceptance testing

Do not leave the final validation environment undefined.

4. Define scope-change rules

For example:

Any feature requiring new database state, new UI controls, new persistence logic, scheduling, or workflow changes is evaluated separately as a scope change.

5. Build preflight validation first

Before live testing:

Configuration ✓
Database ✓
API ✓
External credentials ✓
Webhook ✓
Health ✓

Only then trigger external activity.


Final Takeaway

This project was a mixed experience.

Technically, it was valuable.

The architecture was built, the application was deployed, the integration was hardened, and the milestone deliverables were completed.

The experience also reinforced something more important than any framework or cloud tool:

Good engineers do not simply automate every instruction they receive.

We validate.

We question.

We document.

We define boundaries.

And when a requirement moves outside a safe or acceptable boundary, we stop rather than blindly executing it.

That is not just risk management.

That is professional engineering.


Technologies Used

Twilio · NestJS · Next.js · PostgreSQL · Docker · Docker Compose · Ubuntu · Webhooks · REST APIs · DTMF · IVR Automation · VPS Deployment · SSH · Nginx · Let's Encrypt

Stuck on your project? Get expert guidance for under $10. Let's talk.

Name

Email *

Message *

The Future of GenAI, Cybersecurity, and VoIP: What You Need to Know

I Delivered the Automation. The Final Test Changed Everything

  Freelance engineering projects rarely fail because of code alone. Sometimes the architecture works. The deployment works. The integrations...