Skip to content
Back to writing
11 min read
#energy-tech #fintech-infrastructure #engineering-leadership #architecture

Building Upgrid

Co-founding Upgrid as CTO: a Swiss energy-community platform where billing law, metering data, and go-to-market share the same correctness bar as fintech.

When Switzerland opened the door to Lokale Energiegemeinschaften in 2025, most of the conversation was about solar panels and lower bills. Fair enough. But the product we had to build wasn’t a landing page with a signup form. It was a platform where grid operators exchange metering data on fixed schedules, allocation runs on fifteen-minute intervals, invoices carry Swiss QR-bill references, and a wrong VAT line is a customer complaint and an accounting problem.

I co-founded Upgrid as CTO in January 2025. The job wasn’t “build an app.” It was build the billing, allocation, and operator workflows that make community energy legally and financially real — and ship fast enough to matter while Swiss reform was still fresh.

Real money on real invoices. Same correctness bar I learned at N26, different domain.
Regulatory context

Upgrid operates in Swiss energy law: ZEV, LEG, and vZEV community models each carry different metering, allocation, and billing rules. The platform has to support all of them without pretending they’re the same workflow with different labels.

Who the platform serves

Every product decision at Upgrid starts with who you’re optimizing for. Consumers want zero friction. On the supply side we support three operator models — and they want opposite things from the same codebase:

Consumers

Zero-friction signup. Find a community, understand the savings, sign a contract, done. They will not read a manual about Swiss grid topology.

Full operator accounts

Grid operators and energy retailers running communities end-to-end: community formation, meter wiring, allocation review, invoice generation, Bexio sync, exception handling. They live in the product daily.

Co-brand operators

Municipalities and solar installers who bring members but don't run billing themselves — Upgrid operates the communities on their behalf. Branded join flows and contract-intent triage, one platform underneath.

Affiliates

Partners who refer consumers and earn a cut. No operator console, no community management — just tracked signups and attribution. Lightweight by design.

As CTO I own the architecture, but I also act as PM and marketing when the bottleneck is clarity — not code. That means savings calculators, LEG explainer content, signup funnel instrumentation, and operator onboarding UX all land on the same roadmap as Postgres migrations.

The architecture bet: monorepo at small-team scale

At N26 I spent years inside a ~200-service estate. The most valuable work I did there was often consolidation — pulling scattered fraud rules into one stateful stream because distribution was causing latency, not defending against it.

Upgrid is the opposite call. We’re a small team shipping a regulated, money-handling platform. A 37-package monorepo with shared types, one Postgres schema, and one CI pipeline beats microservice coordination when real coupling is high and team size is low.

N26 (~200 services)
  • High team count — service boundaries match org boundaries
  • Distributed state stitched across Kafka topics and REST calls
  • Deploy coordination across dozens of repos
  • Consolidation became the performance win
Upgrid (monorepo)
  • Small team — shared schema and types reduce integration tax
  • One Postgres database with RLS multi-tenancy
  • Single CI pipeline with ratchets, not big-bang migrations
  • Coupling is real; pretending it's microservices wouldn't help

Team size and actual coupling decide topology. Not fashion.

The money path

Community energy isn’t CRUD. The path from grid data to customer invoice crosses ingestion, allocation, billing, PDF generation, and accounting sync — and every step has a Swiss-specific rule attached.

Where bugs hurt

A UI typo is embarrassing. A wrong allocation on a large vZEV community is an OOM crash, a support ticket, and a recalculation job. A wrong VAT line on a Swiss QR-bill is an invoice dispute. We treat the money path with the same paranoia I brought from financial crime prevention — observability on ingestion, idempotent jobs, and tests on the billing engine before we polish marketing pages.

Contract intents vs live contracts is another example. A prospect signing up through a co-brand operator or affiliate link isn’t a billing customer yet. Mixing those lifecycles in one table would have been convenient and wrong. We separated them early — distinct states, distinct workflows — because retrofitting domain boundaries after invoices exist is expensive.

Engineering multipliers

Raw commit counts don’t compound. Infrastructure and practices do. These are the decisions that let a small team ship like a larger one without lowering the correctness bar.

Company brain

An internal agent in Slack — schema digest, versioned metric definitions, codebase index, read-only connectors to production data, Linear, and PostHog. Ops and product can ask whether a meter is receiving data or how signup traffic looked this month without interrupting an engineer.

Infrastructure as code

CDKTF stacks for Better Stack monitors and heartbeats on the SDAT ingestion pipeline, plus PostHog feature flags defined in the repo and deployed via GitHub Actions. Observability and rollout config are versioned and reviewable — not click-ops in vendor UIs.

Feature flags and experiments

Triple sync: infrastructure definitions, application constants, and a CI check that fails if they drift. The same PostHog project that powers GTM analytics also gates bill-upload onboarding, operator features, and landing-page experiments.

CI ratchets and ADRs

A layered data-access ADR with a check that fails CI on new direct database calls while grandfathering legacy sites. Architecture improves weekly without a big-bang rewrite or winning arguments in meetings.

Metrics as code

Business metric definitions live as YAML in the product repo and sync downstream. The company brain queries known metrics — communities, contracts, meter readings — without ad-hoc SQL every time someone asks a question.

Agent harness

AGENTS.md, Cursor rules, migration gates, and test requirements in CI. AI-assisted delivery is safe to merge in money-handling code because the guardrails are encoded, not implied.

Click-ops rollout
  • Feature flags toggled in vendor UI — no PR, no review
  • Monitors configured by hand — drift from prod
  • Architecture debates in Slack — no enforcement
  • Ops questions routed to whoever is online
Versioned multipliers
  • Flags in IaC + app code + CI sync check
  • Monitors and heartbeats in Terraform state
  • Ratchets fail CI on new violations only
  • Company brain answers from live indexed knowledge
DefineDay 0

ADR or flag definition in infrastructure config. App constants and preload list updated in the same PR.

GateCI

check:feature-flags and check:data-access run on every pull request. Drift fails the build.

Roll outProd

Gradual flag rollout or PostHog experiment. Operator features ship dark; consumer flows get A/B variants.

LearnPostHog

Funnel events, field errors, abandonments — same instrumentation stack as GTM analytics.

The unglamorous work matters too: locale drift checks, Supabase typegen drift detection on PRs, migration timestamp validation. Nobody writes blog posts about those. They keep a monorepo honest.

Applied AI — shipped, not demoed

I’ve seen too many “AI-native” pitches that mean “we use Copilot.” Upgrid has three layers, ordered by who benefits:

Demo AI
  • Chatbot on the marketing site
  • Internal Slack bot with no prod access
  • Copilot with no CI gates on output
  • No connection to business metrics
Shipped AI
  • Bill-upload pre-fill: customer uploads energy bill, LLM extracts fields, form arrives populated
  • Company brain: ops self-serve against live data and indexed schema
  • Agent harness: migration rules, test gates, touch-it-migrate-it policy in CI
Bill upload

The highest-friction step in consumer onboarding is typing your meter details correctly. We run bill extraction through the Vercel AI Gateway with structured output and validation before anything hits the contract intent. PostHog tracks the full bill-upload funnel — upload started, extraction succeeded, fields accepted — alongside the join flow.

Layer one is customer-facing. Layer two is company-facing. Layer three is engineering-facing. All three need guardrails; only the first two need to work when I’m not in the room.

Beyond the keyboard

Engineering multipliers don’t matter if nobody signs up. As co-founder I spent serious time on go-to-market: a savings calculator, LEG reform content, SEO guides, co-brand landing pages for municipalities and installers, affiliate attribution, and signup instrumentation built like production software.

2k+
community members
250+
communities
150+
municipalities
Multi-channel
referral, direct, organic, paid
LandingAcquisition

Referral, direct, organic social, paid search, and organic search — no single growth hack. Co-brand pages for municipalities and installers, affiliate links, and Upgrid-owned SEO content all feed the same join flow.

FormInstrumented

join_community.* events track steps, field errors, abandonments, and CTA clicks — not just pageviews. The taxonomy grew over time; early dashboards undercounted what we can measure today.

ContractSubmission

Contract intents from web, operator-assisted onboarding, and partner channels. Product scale is north of two thousand members — web-only funnel snapshots never captured the full picture.

CommunityAssignment

Auto-selection, operator triage, contract intent → live contract when preconditions are met.

Analytics bolted on
  • Pageviews only — no funnel steps
  • Experiments configured separately from flags
  • GTM and engineering use different tools
  • No field-level error tracking
Instrumentation as product
  • Rich join_community.* event taxonomy
  • Same PostHog project for flags and funnels
  • Bill-upload and contract_intent.* operator events
  • Funnel conversion and drop-off measured in-product

2k+ members, 250+ communities, and 150+ municipalities on upgrid.ch are the numbers worth quoting — they reflect actual product scale. PostHog is how we iterate on the join flow week to week, but the event taxonomy evolved; point-in-time funnel exports from early instrumentation undercount submissions that came through operator onboarding, co-brand partners, and channels we hadn’t wired yet.

What I’d do again / differently

Observability and IaC on ingestion from day one

SDAT is the root of the money path. Better Stack monitors and heartbeats should have shipped with the first ingestion job, not after the first silent failure.

Treat grid data contracts like a schema registry

Metering formats and allocation inputs change. Version the contracts, fail loudly on drift, and document them where the company brain can find them.

Ship operator UX before consumer polish

Operators live in the product. A beautiful consumer landing page doesn't help if community formation and billing exceptions are painful.

Run more experiments when regulation allows

Feature flags and PostHog experiments are multipliers — use them for join flows, bill upload, and operator features instead of debating in meetings.


Local energy is a systems, regulation, and go-to-market problem — not a landing page. The teams that win will be the ones who treat billing correctness and signup instrumentation with the same seriousness as fintech treats fraud.

The thesis
RR
Rafael Roman
CTO & Co-founder at Upgrid · Previously N26, Personio, GFT

More writing