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.
| Layer | Technology | Why |
|---|---|---|
| Backend API | Ruby on Rails 8 | Rapid development, excellent ORM, mature ecosystem |
| Frontend | React 18 | Component model, ecosystem, developer familiarity |
| Database | PostgreSQL 16 | JSONB for schema storage, reliability, performance |
| Cache / Queue | Redis 7 | Sidekiq backend, caching, pub/sub |
| Background Jobs | Sidekiq 7 | Reliable job processing, scheduling, retries |
| Real-time | Action Cable | Native Rails integration, webhook tunnel |
| Search | PostgreSQL full-text | No external dependency, good enough for our scale |
Rails is not the trendy choice in 2026, but it lets a small team move fast. PostgreSQL's JSONB support is critical for storing and querying JSON schemas without a separate document database.
The schema diff engine is the core of Depify's API Intelligence module. It works in two stages.
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
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.
The webhook debugger identifies which provider sent a webhook using a regex-based detection system that examines headers and body structure.
Detected via stripe-signature header or event type pattern matching.
Detected via x-github-event or x-hub-signature-256 headers.
Detected via x-shopify-topic or x-shopify-hmac-sha256 headers.
Detected via AccountSid starting with "AC" or MessageSid starting with "SM".
The flag evaluation engine uses a 5-tier chain that runs in strict order.
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
Many API providers do not have structured changelogs. They announce changes in blog posts, sometimes buried several pages deep.
The scraper runs every 6 hours. For providers with RSS feeds, we prefer RSS for speed, falling back to HTML scraping only when needed.
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
The webhook forwarding tunnel uses Rails Action Cable to establish a persistent connection between the Depify server and the developer's local machine.
No port forwarding, no dynamic DNS, no firewall configuration needed. Auto-reconnects on network interruptions.
| Queue Priority | Jobs |
|---|---|
| Critical | API checks, alert delivery |
| Default | Schema diffs, anomaly detection |
| Low | Provider scraping, data cleanup |
| 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 throughput | 10,000+ jobs/minute |
"We chose Rails because it lets us ship features fast and PostgreSQL because it handles both relational data and JSON documents well. The boring technology stack lets us focus on solving interesting problems."
Move flag evaluation to edge nodes for sub-millisecond latency globally.
Accept traces and metrics via OTLP for teams already using OTel collectors.
Automatically map the relationships between your services and third-party APIs.
Long-term performance trends with regression detection across all endpoints.
If you have questions about our architecture, reach out at contact@depify.io.
See Depify in action. Free tier available with no credit card required.
Start Free Trial