Software Engineer
Location: Remote
As an Engineer at Skillshare, you’ll shape how teachers share their expertise, how students discover and watch content they love, and how our platform shows up to the world. This role sits at the foundations of our backend — the domain model, the service architecture, the event backbone, and the conventions every other engineer builds on top of.
You’ll work in a single TypeScript monorepo built on hexagonal architecture, Domain-Driven Design, and Clean Architecture. Our services are NestJS, our API is code-first GraphQL, our data lives in Postgres behind Prisma, our domain events travel through a transactional outbox
into Pub / Sub, and everything runs on Cloud Run defined in Pulumi. Each bounded context is packaged as a domain core plus swappable adapters — an in-memory adapter for tests, a Prisma / Postgres adapter for persistence, provider adapters for third parties — so the domain never learns what technology is underneath it.
This is a role for someone who thinks in aggregates and boundaries, writes the decision record before the code, and leaves behind a codebase that the next engineer can navigate without a guided tour.
Your expertise:
- You have 8+ years building backend systems in production, with deep TypeScript and Node.js experience and the judgement that comes from having maintained your own designs for years
- You are proficient with an agentic coding harness — Claude Code, or a comparable tool — as your normal way of building software, not as occasional autocomplete. You’re comfortable with a model writing the majority of the code while you own the design, the review, and the result
- You review generated code more sceptically, not less. You catch the plausible-but-wrong, notice the quietly dropped edge case or the invariant that stopped being enforced, and never let something land because it read confidently. Whoever typed it, you’re accountable for what you merge
- You’ve practised Domain-Driven Design on a real, messy domain — not just read the book. You can find an aggregate boundary, defend it, and explain why a table is not an aggregate and a service is not a bounded context
- You’ve built systems on hexagonal architecture / ports and adapters, you understand the dependency rule and why it points inward, and you know both the cost of that indirection and when it’s worth paying
- You’re strong in NestJS — modules, providers, dependency injection, lifecycle, testing — or you’ve built and maintained production services in another Node.js backend framework (Fastify, Express, Hono, AdonisJS, tRPC) and would pick Nest up quickly. This is a Node.js and TypeScript role end to end; depth in the JVM or .NET ecosystems doesn’t substitute for it
- You’ve designed and owned a GraphQL API, not just added resolvers to one: schema modelling, the N+1 problem and batching, pagination, error semantics, and evolving a live schema without breaking clients
- You’ve worked in event-driven systems and can talk concretely about at-least-once delivery, idempotency, ordering, and consumer retries — and about why writing to the database and publishing to a broker in the same breath is a bug, not a shortcut
- You’re fluent in relational data modelling and SQL — schema design, migrations against live data, indexing, and transaction boundaries that are chosen rather than inherited
- You have strong testing instincts: you use fast in-memory implementations of your own ports for unit tests, reserve integration tests for the seams that actually integrate, and can work test-first when the design is uncertain
- You communicate design clearly — in writing and visually. This role produces decision records, domain glossaries, and standards that other engineers read and follow, so arguing a technical position clearly in prose is a hard requirement, not a soft skill. The visual half matters just as much: architecture diagrams, data and event flows, and context maps are how a distributed design gets reviewed before it is built — and how understanding of it spreads across the team afterwards. We keep ours in Miro, committed beside the documents they explain, and work in Miro when a model still needs to be thought through with people rather than published to them
- You work with real autonomy — you can take an ambiguous product need, model it, sequence it, and drive it to done without waiting to be handed a spec
- You can overlap with our US-based team until at least 1:00 PM Eastern each day (8:00 PM if you’re based in Ukraine), which is when code review, pairing, and our Monday goal-setting and Friday demo happen. Outside that window we work asynchronously, so strong written English and the habit of leaving good context behind in issues and pull requests matter as much as the overlap itself
Will definitely be a plus:
- You’ve shaped a codebase for agents — authored rules or context files (CLAUDE.md, skills, subagent definitions), wired up MCP servers or hooks, or built the lint, type, and test guardrails that let a harness run with less supervision
- Prisma, and a considered opinion on ORMs — where the data-mapper boundary belongs, and how to keep a Unit of Work honest across repositories
- Fine-grained authorization systems: OpenFGA, Zanzibar-style ReBAC, or policy engines like OPA — and the modelling instincts to know when relationships beat roles. — GCP — Cloud Run, Pub / Sub, Cloud SQL — and Infrastructure as Code in Pulumi (TypeScript); prior AWS and Terraform experience translates fine
- Secrets management done properly: SOPS, KMS-backed encryption, Workload Identity Federation, and a preference for keyless auth over credential files
- Change data capture, stream processing, or having run a transactional outbox in production and dealt with its failure modes
- Monorepo experience with a task runner and shared libraries (we use Nx + Bun), including writing your own generators or plugins
- Schema-driven contracts with Zod or similar, especially for validating events at the boundary
- Running Nest on a non-Express runtime (Hono, Fastify), or building for edge and Workers-style constraints
- Search infrastructure — Typesense, Elasticsearch, OpenSearch, vector retrieval, or query-intent classification
- Subscriptions and billing domain experience: providers such as Recurly, Stripe, or RevenueCat, entitlement modelling, webhook verification, and provider-authoritative state
- Observability in practice — OpenTelemetry instrumentation, tracing across async boundaries, and debugging from traces rather than guesswork
- BDD with Gherkin, or other executable-specification approaches that keep product intent and tests in one place
- Enough React and TypeScript on the client to consume your own APIs and feel the ergonomics you’re handing to frontend engineers
What’s in it for you?
- Your impact: You’d be working at the layer where architectural decisions either compound or come due — the domain model and the platform patterns that every feature is built through
- Deliberately built: Hexagonal, DDD, and Clean Architecture aren’t aspirational words in a wiki here; the package structure, the linting, and the review standards all enforce them. If you’ve spent years arguing for this kind of rigour, you’d be arguing with people who already agree
- Real scope: Domain modelling, API contracts, event infrastructure, data design, authorization, and the developer tooling around all of it. Broad enough that you choose where to go deep
- Our team: A passionate, creative, and supportive group that values collaboration and craftsmanship
- Our mission: We connect lifelong learners around the world and empower them to pursue creativity and growth
You will be involved into:
- Model the domain: define bounded contexts, aggregates, value objects, and domain events, and hold the line on a ubiquitous language that product, design, and code all share
- Build with an AI coding harness as your primary tool. Most of the code here is written by an agent — Claude Code, driven by the skills, CLAUDE.md, and CONTEXT.md files checked into the repo alongside the code. Directing, reviewing, and correcting that output is the job; typing every line is not. In this role your leverage compounds through the substrate you maintain — the decision records, glossaries, typed contracts, and tests that let an agent produce work which is correct by construction rather than merely plausible
- Keep the domain isolated behind ports and implement the adapters at the edges — persistence, messaging, search, third-party providers — so infrastructure can be swapped without touching business rules
- Design and build NestJS services: modules, providers, dependency injection, use cases that own their transaction boundary, and repositories that keep the read and write paths separate
- Own our GraphQL API as a contract: code-first schema, thin resolvers with no business logic, DataLoader for the N+1 problem, field projection pushed down into read repositories, and a deliberate deprecation path when the schema has to change
- Work on the event backbone: schema-validated domain events, transactional outbox for at-least-once durability alongside the aggregate write, and idempotent consumers so a redelivery doesn’t double-charge anyone
- Model authorization as relationships — fine-grained, resource-level, fail-closed — and expose it to clients as explicit per-resource capability fields rather than roles guessed at in the UI
- Design Postgres schemas and migrations, decide transaction boundaries and isolation levels deliberately, and build read projections that serve queries the write model shouldn’t
- Partner on infrastructure as code — Pulumi, GCP, Cloud Run, secrets management, keyless authentication — because the people designing services should understand how they’re deployed and operated
- Write the decisions down. Architecture decision records, domain glossaries, and standards docs are first-class deliverables here, not afterthoughts. So is the developer tooling that makes the architecture legible: generators, custom task-runner plugins, and tools that surface the domain model straight from source
- Work to our weekly cadence — we set high-order goals together on Monday and deliver against them in a demo on Friday, which means breaking foundational work into slices that show something real by the end of the week rather than disappearing into a month-long refactor
- Set the bar in code review and testing — unit tests against in-memory adapters, integration tests at the seams, executable specs for behaviour — and mentor engineers into the patterns until they don’t need you to explain them
About the company and project:
ZONE3000 is a 2400+ people family that forms a new cultural code in the software development business. For 25 years we have been focusing on the highest quality of projects and empowering people to think big and make a difference. We are looking for talents who want to create and improve technological solutions for tomorrow and make things as best possible.
Skillshare is an online learning community whose mission is to connect curious, lifelong learners everywhere — and, in doing so, build a more creative, more generous, and more prosperous world. Today, millions of members come to Skillshare to learn creative and entrepreneurial skills, connect with peers, and even teach a class themselves.
Відгукнутись на вакансію