Skip to content
Skillv1.0.0

linear-reference-architecture

Production-grade Linear integration architecture patterns. Use when designing system architecture, choosing integration patterns, or reviewing architectural decisions for Linear integrations. Trigger:

by jeremylongshore(0) 0 installs
Free
Sign in to install

Free account. Installing gives you the manifest plus copy-paste snippets.

See reviews

About

Imported from jeremylongshore/tons-of-skills-marketplace (plugins/saas-packs/linear-pack/skills/linear-reference-architecture/SKILL.md). Install upstream with npx skills add jeremylongshore/tons-of-skills-marketplace --skill linear-reference-architecture. Copyright stays with the author (MIT).

Linear Reference Architecture

Overview

Production-grade architectural patterns for Linear integrations. Choose the right pattern based on team size, complexity, and real-time requirements.

Architecture Decision Matrix

Pattern Best For Complexity Rate Budget Example
Simple Single app, small team Low < 500 req/hr Internal dashboard
Service-Oriented Multiple apps, shared state Medium 500-2,000 req/hr Platform with Linear sync
Event-Driven Real-time needs, many consumers High < 500 req/hr + webhooks Multi-service notification system
CQRS Audit trails, complex queries Very High Minimal API calls Compliance-grade tracking

Architecture 1: Simple Integration

Direct SDK calls from your application. Best for scripts, internal tools, and prototypes.

// src/linear.ts — single module, shared client
import { LinearClient } from "@linear/sdk";

const client = new LinearClient({ apiKey: process.env.LINEAR_API_KEY! });

// Direct SDK calls from any part of your app
export async function getOpenIssues(teamKey: string) {
  return client.issues({
    first: 50,
    filter: {
      team: { key: { eq: teamKey } },
      state: { type: { nin: ["completed", "canceled"] } },
    },
    orderBy: "priority",
  });
}

export async function createBugReport(teamId: string, title: string, description: string) {
  const labels = await client.issueLabels({ filter: { name: { eq: "Bug" } } });
  return client.createIssue({
    teamId,
    title,
    description,
    priority: 2,
    labelIds: labels.nodes.length ? [labels.nodes[0].id] : [],
  });
}

Architecture 2: Service-Oriented with Gateway

Centralized Linear access through a gateway service with caching and rate limiting.

// src/linear-gateway.ts
import { LinearClient } from "@linear/sdk";

class LinearGateway {
  private client: LinearClient;
  private cache = new Map<string, { data: any; expiresAt: number }>();
  private requestQueue: Array<{ fn: () => Promise<any>; resolve: Function; reject: Function }> = [];
  private processing = false;

  constructor(apiKey: string) {
    this.client = new LinearClient({ apiKey });
  }

  // Cached reads
  async getTeams() {
    return this.cachedQuery("teams", () => this.client.teams().then(r => r.nodes), 600);
  }

  async getStates(teamId: string) {
    return this.cachedQuery(`states:${teamId}`, async () => {
      const team = await this.client.team(teamId);
      return (await team.states()).nodes;
    }, 1800);
  }

  // Rate-limited writes
  async createIssue(input: any) {
    return this.enqueue(() => this.client.createIssue(input));
  }

  async updateIssue(id: string, input: any) {
    return this.enqueue(() => this.client.updateIssue(id, input));
  }

  // Custom queries through the gateway
  async rawQuery(query: string, variables?: any) {
    return this.enqueue(() => this.client.client.rawRequest(query, variables));
  }

  // Cache invalidation (called from webhook handler)
  invalidate(pattern: string) {
    for (const key of this.cache.keys()) {
      if (key.startsWith(pattern)) this.cache.delete(key);
    }
  }

  private async cachedQuery<T>(key: string, fn: () => Promise<T>, ttlSec: number): Promise<T> {
    const cached = this.cache.get(key);
    if (cached && Date.now() < cached.expiresAt) return cached.data;
    const data = await this.enqueue(fn);
    this.cache.set(key, { data, expiresAt: Date.now() + ttlSec * 1000 });
    return data;
  }

  private async enqueue<T>(fn: () => Promise<T>): Promise<T> {
    return new Promise((resolve, reject) => {
      this.requestQueue.push({ fn, resolve, reject });
      if (!this.processing) this.processQueue();
    });
  }

  private async processQueue() {
    this.processing = true;
    while (this.requestQueue.length > 0) {
      const { fn, resolve, reject } = this.requestQueue.shift()!;
      try { resolve(await fn()); } catch (e) { reject(e); }
      if (this.requestQueue.length > 0) {
        await new Promise(r => setTimeout(r, 100)); // 10 req/sec max
      }
    }
    this.processing = false;
  }
}

export const gateway = new LinearGateway(process.env.LINEAR_API_KEY!);

Architecture 3: Event-Driven

Webhook-centric architecture. Minimal API calls, real-time processing.

// src/event-processor.ts
import express from "express";
import crypto from "crypto";
import { EventEmitter } from "events";

// Internal event bus
const bus = new EventEmitter();

// Webhook ingester
const app = express();
app.post("/webhooks/linear", express.raw({ type: "*/*" }), (req, res) => {
  const sig = req.headers["linear-signature"] as string;
  const body = req.body.toString();
  const expected = crypto.createHmac("sha256", process.env.LINEAR_WEBHOOK_SECRET!)
    .update(body).digest("hex");

  if (!crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) {
    return res.status(401).end();
  }

  const event = JSON.parse(body);
  res.json({ ok: true });

  // Emit to internal consumers
  bus.emit(`${event.type}.${event.action}`, event);
  bus.emit(event.type, event);
  bus.emit("*", event);
});

// Consumer: Slack notifications
bus.on("Issue.update", async (event) => {
  if (event.updatedFrom?.stateId && event.data.state?.type === "completed") {
    await notifySlack(`Done: ${event.data.identifier} ${event.data.title}`);
  }
});

// Consumer: Database sync
bus.on("Issue", async (event) => {
  if (event.action === "create") await db.issues.insert(event.data);
  if (event.action === "update") await db.issues.update(event.data.id, event.data);
  if (event.action === "remove") await db.issues.softDelete(event.data.id);
});

// Consumer: Cache invalidation
bus.on("*", (event) => {
  gateway.invalidate(event.type.toLowerCase());
});

Architecture 4: CQRS with Local State

Separate read and write paths. Full local state for complex queries, API for writes.

// Write side: mutations go through Linear API
async function createIssue(input: any) {
  const result = await gateway.createIssue(input);
  // Local state updated via webhook, not here
  return result;
}

// Read side: queries against local database (no API calls)
async function getSprintVelocity(teamKey: string, sprints: number) {
  return db.query(`
    SELECT c.name, SUM(i.estimate) as velocity
    FROM cycles c
    JOIN issues i ON i.cycle_id = c.id AND i.state_type = 'completed'
    WHERE c.team_key = ? AND c.completed_at IS NOT NULL
    ORDER BY c.completed_at DESC
    LIMIT ?
  `, [teamKey, sprints]);
}

// Sync: webhook events keep local state fresh
// Full sync: daily consistency check (see linear-data-handling)

Project Structure

src/
  linear/
    gateway.ts          # Rate-limited, cached API access
    webhook-handler.ts  # Signature verification + routing
    event-bus.ts        # Internal event distribution
    cache.ts            # TTL cache with invalidation
  services/
    issue-service.ts    # Business logic
    sync-service.ts     # Data synchronization
  config/
    linear.ts           # Environment config + validation

Error Handling

Error Cause Solution
Rate limit exceeded Too many direct API calls Route all calls through gateway
Stale cache TTL too long, missed webhook Webhook invalidation + periodic full sync
Event loss Webhook delivery failure Idempotent handlers + consistency checks
Schema drift SDK version mismatch Pin version, test upgrades in staging

Resources

Use it

Copy one of these into your project. Installing also returns the manifest and these snippets.

yaml
targets:
  - https://api.opensmartroute.ai/api/v1/registry/jeremylongshore-tons-of-skills-marketplace-linear-refere-bc6c01/manifest   # or paste the manifest below

Manifest

An Open Capability Manifest: the router reads it to know what this does, what it costs and when to pick it.

jeremylongshore-tons-of-skills-marketplace-linear-refere-bc6c01.ocm.jsonjson
{
  "ocm": "1",
  "id": "jeremylongshore-tons-of-skills-marketplace-linear-refere-bc6c01",
  "kind": "skill",
  "name": "linear-reference-architecture",
  "description": "Production-grade Linear integration architecture patterns. Use when designing system architecture, choosing integration patterns, or reviewing architectural decisions for Linear integrations. Trigger: \"linear architecture\", \"linear system design\", \"linear integration patterns\", \"linear best practices architecture\".",
  "publisher": "jeremylongshore",
  "version": "1.0.0",
  "capabilities": {
    "domains": [
      "general"
    ],
    "tags": [
      "skill-md",
      "saas",
      "linear",
      "linear-reference",
      "skills-sh"
    ],
    "languages": [
      "en"
    ]
  },
  "quality_prior": 0.6,
  "examples": [
    "Production-grade Linear integration architecture patterns. Use when designing system architecture, choosing integration patterns, or reviewing architectural decisions for Linear integrations. Trigger: \"linear architecture\", \"linear system design\", \"linear integration patterns\", \"linear best practices architecture\"."
  ],
  "primary": false,
  "metadata": {
    "source": {
      "provider": "skills.sh",
      "repository": "https://github.com/jeremylongshore/tons-of-skills-marketplace",
      "path": "plugins/saas-packs/linear-pack/skills/linear-reference-architecture/SKILL.md",
      "ref": "HEAD",
      "url": "https://github.com/jeremylongshore/tons-of-skills-marketplace/blob/HEAD/plugins/saas-packs/linear-pack/skills/linear-reference-architecture/SKILL.md",
      "key": "jeremylongshore/tons-of-skills-marketplace/plugins/saas-packs/linear-pack/skills/linear-reference-architecture/SKILL.md"
    },
    "compatibility": "Designed for Claude Code",
    "allowed_tools": [
      "Read,",
      "Write,",
      "Edit,",
      "Grep"
    ],
    "license": "MIT"
  },
  "instructions": "# Linear Reference Architecture\n\n## Overview\n\nProduction-grade architectural patterns for Linear integrations. Choose the right pattern based on team size, complexity, and real-time requirements.\n\n## Architecture Decision Matrix\n\n| Pattern | Best For | Complexity | Rate Budget | Example |\n|---------|----------|------------|-------------|---------|\n| Simple | Single app, small team | Low | < 500 req/hr | Internal dashboard |\n| Service-Oriented | Multiple apps, shared state | Medium | 500-2,000 req/hr | Platform with Linear sync |\n| Event-Driven | Real-time needs, many consumers | High | < 500 r",
  "cost": {
    "context_tokens": 1955
  }
}

Fetch it by URL: GET /api/v1/registry/jeremylongshore-tons-of-skills-marketplace-linear-refere-bc6c01/manifest?version=1.0.0

Reviews

Star ratings from people who tried it. One review per account; edit yours any time.

No reviews yet. Install it, try it, and be the first to rate it.