Building Depify: Technical Deep Dive


We believe in transparency about how we build software. This post is a technical walkthrough of Depify's architecture: the tech stack, the engineering decisions, and the systems that power each module.

The Stack
Layer Technology Why
Backend APIRuby on Rails 8Rapid development, excellent ORM, mature ecosystem
FrontendReact 18Component model, ecosystem, developer familiarity
DatabasePostgreSQL 16JSONB for schema storage, reliability, performance
Cache / QueueRedis 7Sidekiq backend, caching, pub/sub
Background JobsSidekiq 7Reliable job processing, scheduling, retries
Real-timeAction CableNative Rails integration, webhook tunnel
SearchPostgreSQL full-textNo external dependency, good enough for our scale
Schema Diff Engine

The schema diff engine is the core of Depify's API Intelligence module. It works in two stages.

Stage 1: JSON Schema Inference

Given a JSON response body, the engine infers a JSON Schema that describes its structure. This handles nested objects, nullable fields, enum detection, optional vs. required fields, and array item types.

class SchemaInferrer
  def infer(value, path = "$")
    case value
    when Hash
      {
        type: "object",
        properties: value.transform_values { |v| infer(v) },
        required: value.keys
      }
    when Array
      {
        type: "array",
        items: value.empty? ? {} : infer(value.first)
      }
    when String  then { type: "string" }
    when Integer then { type: "integer" }
    when Float   then { type: "number" }
    when TrueClass, FalseClass then { type: "boolean" }
    when NilClass then { type: "null" }
    end
  end
end
Stage 2: Field-Level Comparison

The diff engine walks both schema trees simultaneously and produces a list of changes classified by severity.

{
  "changes": [
    {
      "path": "$.data.user.phone",
      "change_type": "field_removed",
      "severity": "breaking"
    },
    {
      "path": "$.data.user.phone_number",
      "change_type": "field_added",
      "severity": "additive"
    },
    {
      "path": "$.data.amount",
      "change_type": "type_changed",
      "severity": "breaking",
      "baseline": { "type": "string" },
      "current": { "type": "integer" }
    }
  ]
}

Schema comparisons are cheap -- typically under 5ms for schemas with up to 500 fields.

Smart Webhook Parsers

The webhook debugger identifies which provider sent a webhook using a regex-based detection system that examines headers and body structure.

Stripe

Detected via stripe-signature header or event type pattern matching.

GitHub

Detected via x-github-event or x-hub-signature-256 headers.

Shopify

Detected via x-shopify-topic or x-shopify-hmac-sha256 headers.

Twilio

Detected via AccountSid starting with "AC" or MessageSid starting with "SM".

Feature Flag Evaluator

The flag evaluation engine uses a 5-tier chain that runs in strict order.

1. Kill Switch
2. Prerequisites
3. Individual Targeting
4. Segment Match
5. % Rollout

A typical evaluation completes in under 2ms, including database lookup cached in Redis with a 10-second TTL.

# Evaluation performance characteristics
Average evaluation time: 1.2ms
P99 evaluation time:     4.8ms
Redis cache hit rate:    94%
Database fallback:       ~8ms
Deep Scraper for Provider Changes

Many API providers do not have structured changelogs. They announce changes in blog posts, sometimes buried several pages deep.

1. Discover Pages
2. Crawl Pagination
3. Extract Content
4. Classify Changes
5. Deduplicate

The scraper runs every 6 hours. For providers with RSS feeds, we prefer RSS for speed, falling back to HTML scraping only when needed.

Anomaly Detection

Depify uses Z-score statistical analysis. The detector adapts to each endpoint's individual performance profile.

class AnomalyDetector
  WINDOW_SIZE = 168  # 7 days of hourly data points

  def detect(endpoint, current_value)
    history = endpoint.latency_history(
      window: WINDOW_SIZE, percentile: :p95
    )
    return nil if history.size < 24

    mean = history.sum / history.size.to_f
    stddev = Math.sqrt(
      history.sum { |v| (v - mean) ** 2 } / history.size.to_f
    )
    return nil if stddev < 1

    z_score = (current_value - mean) / stddev

    if z_score.abs > 3
      create_anomaly_alert(endpoint, z_score, current_value, mean)
    elsif z_score.abs > 2
      create_anomaly_warning(endpoint, z_score, current_value, mean)
    end
  end
end
Real-Time: Action Cable WebSocket Tunnel

The webhook forwarding tunnel uses Rails Action Cable to establish a persistent connection between the Depify server and the developer's local machine.

CLI connects via WSS
Webhook arrives
Server pushes via WS
CLI forwards to localhost
Response sent back

No port forwarding, no dynamic DNS, no firewall configuration needed. Auto-reconnects on network interruptions.

Background Job Pipeline
Queue Priority Jobs
CriticalAPI checks, alert delivery
DefaultSchema diffs, anomaly detection
LowProvider scraping, data cleanup
Performance Characteristics
Metric Value
API check latency (internal)< 50ms
Flag evaluation latency< 5ms (P99)
Webhook capture to dashboard< 200ms
Schema diff computation< 5ms (500 fields)
Alert delivery (Slack/email)< 2 seconds
WebSocket tunnel overhead< 100ms added
Background job throughput10,000+ jobs/minute
What is Next
Edge Flag Evaluation

Move flag evaluation to edge nodes for sub-millisecond latency globally.

OpenTelemetry Receiver

Accept traces and metrics via OTLP for teams already using OTel collectors.

API Dependency Graph

Automatically map the relationships between your services and third-party APIs.

Historical Trends

Long-term performance trends with regression detection across all endpoints.

If you have questions about our architecture, reach out at contact@depify.io.

Try the Platform We Built

See Depify in action. Free tier available with no credit card required.

Start Free Trial