Live
โ€ขBlack Hat USADark Readingโ€ขBlack Hat AsiaAI Businessโ€ข2 New Orleans city attorneys resign after ChatGPT was used to help prepare federal court filing - WWLTV.comGoogle News: ChatGPTโ€ขI Brute-Forced 2 Million Hashes to Get a Shiny Legendary Cat in My Terminal. It Has Max SNARK and a Propeller Hat.DEV Communityโ€ขHave to do enough for my talk, "Is AI Getting Reports Wrong? Try Google LookML, Your Data Dictionary!" at Google NEXT 2026DEV Communityโ€ขOpenAI Foundation: $1 Billion Investment To Scale AI-Driven Philanthropy Across Health, Jobs, And Safety - Pulse 2.0GNews AI AGIโ€ขTaming the Ingredient Sourcing Nightmare with AI AutomationDEV Communityโ€ข# ๐Ÿš€ How to Build a High-Performance Landing Page with Next.js 15 and Tailwind v4DEV Communityโ€ขPrediction: Nvidia's Vera Rubin Platform Will Create at Least 2 New Artificial Intelligence (AI) Millionaire-Maker Stocks by the End of 2026 - The Motley FoolGoogle News: AIโ€ขClaude Code Architecture Explained: Agent Loop, Tool System, and Permission Model (Rust Rewrite Analysis)DEV Communityโ€ขMarietta high school student designs AI software to streamline employee scheduling - CBS NewsGoogle News: AIโ€ขThe Data Structure That's Okay With Being WrongDEV Communityโ€ขHow to Auto-Index Your URLs with Google Search Console APIDEV Communityโ€ขThe Indestructible FutureLessWrong AIโ€ขBlack Hat USADark Readingโ€ขBlack Hat AsiaAI Businessโ€ข2 New Orleans city attorneys resign after ChatGPT was used to help prepare federal court filing - WWLTV.comGoogle News: ChatGPTโ€ขI Brute-Forced 2 Million Hashes to Get a Shiny Legendary Cat in My Terminal. It Has Max SNARK and a Propeller Hat.DEV Communityโ€ขHave to do enough for my talk, "Is AI Getting Reports Wrong? Try Google LookML, Your Data Dictionary!" at Google NEXT 2026DEV Communityโ€ขOpenAI Foundation: $1 Billion Investment To Scale AI-Driven Philanthropy Across Health, Jobs, And Safety - Pulse 2.0GNews AI AGIโ€ขTaming the Ingredient Sourcing Nightmare with AI AutomationDEV Communityโ€ข# ๐Ÿš€ How to Build a High-Performance Landing Page with Next.js 15 and Tailwind v4DEV Communityโ€ขPrediction: Nvidia's Vera Rubin Platform Will Create at Least 2 New Artificial Intelligence (AI) Millionaire-Maker Stocks by the End of 2026 - The Motley FoolGoogle News: AIโ€ขClaude Code Architecture Explained: Agent Loop, Tool System, and Permission Model (Rust Rewrite Analysis)DEV Communityโ€ขMarietta high school student designs AI software to streamline employee scheduling - CBS NewsGoogle News: AIโ€ขThe Data Structure That's Okay With Being WrongDEV Communityโ€ขHow to Auto-Index Your URLs with Google Search Console APIDEV Communityโ€ขThe Indestructible FutureLessWrong AI

Monorepo Architecture with pnpm Workspace, Turborepo & Changesets ๐Ÿ“ฆ

DEV Communityby Yasin ATEลžApril 1, 202620 min read0 views
Source Quiz

<p>When you're developing a project with multiple packages, managing each one in its own repo can quickly turn into a nightmare. In this article, we'll set up a monorepo architecture from scratch using <strong>pnpm workspace</strong>, speed up build processes with <strong>Turborepo</strong>, and build an automated NPM publish pipeline with <strong>Changesets</strong>.</p> <h2> ๐Ÿ—๏ธ What Is a Monorepo? </h2> <p>Let's say you're building a design system. You have a core package, a theme package, and a utils package. Now imagine keeping all of these in <strong>separate repositories</strong>.</p> <p>When you fix a bug in the core package, what happens? You switch to the theme repo and update the dependency. Then you switch to the utils repo. You open separate PRs for each, wait for separate CI/

When you're developing a project with multiple packages, managing each one in its own repo can quickly turn into a nightmare. In this article, we'll set up a monorepo architecture from scratch using pnpm workspace, speed up build processes with Turborepo, and build an automated NPM publish pipeline with Changesets.

๐Ÿ—๏ธ What Is a Monorepo?

Let's say you're building a design system. You have a core package, a theme package, and a utils package. Now imagine keeping all of these in separate repositories.

When you fix a bug in the core package, what happens? You switch to the theme repo and update the dependency. Then you switch to the utils repo. You open separate PRs for each, wait for separate CI/CD pipelines, and publish separately. Even with just three packages, this process is exhausting โ€” with ten, it's a total nightmare.

This is exactly where the monorepo comes in.

A monorepo is an architectural approach that houses multiple projects under a single repository. All your packages live in the same repo, share the same commit history, and can easily reference each other.

So why does this matter? Let's look at a few key advantages:

โ˜… Code sharing in one place: Shared utilities, types, and configurations are instantly available to all packages. No need to wait for an NPM publish.

โ˜… Atomic changes: You can make a change that affects multiple packages in a single commit. No more cross-repo PR synchronization headaches.

โ˜… Consistent tooling: ESLint, Prettier, and TypeScript configurations are managed from a single source. The question "Why does this repo have different rules?" becomes a thing of the past.

โ˜… Easy onboarding: When a new developer joins the project, a single git clone and pnpm install brings up the entire ecosystem.

๐Ÿ”ง Setting Up a pnpm Workspace

pnpm stands out among Node.js package managers for its monorepo support. Unlike npm and yarn, its content-addressable store keeps all dependencies in a global store and links them into your projects via hard links. This saves a massive amount of disk space.

Let's walk through setting up a monorepo architecture with pnpm workspace using a sample project.

Step 1: Install pnpm and Initialize the Project

Install pnpm globally:

npm install -g pnpm

Enter fullscreen mode

Exit fullscreen mode

Then initialize a new monorepo project:

mkdir my-design-system cd my-design-system pnpm init git init

Enter fullscreen mode

Exit fullscreen mode

โ˜… The pnpm init command creates a package.json file at the root level.

โ˜… This file will serve as the main entry point for your monorepo. All workspace scripts will be managed from here.

Step 2: Create pnpm-workspace.yaml

Create a pnpm-workspace.yaml file in the project root:

packages:

  • "packages/*"
  • "apps/*"
  • "website"`

Enter fullscreen mode

Exit fullscreen mode

โ˜… The packages/* expression defines each folder under the packages directory as a separate workspace package.*

โ˜… apps/* includes applications, and website adds the documentation site to the workspace.*

โ˜… Without this file, pnpm won't recognize your project as a monorepo.

Step 3: Configure the Root package.json

Configure the root package.json for the monorepo:

{  "name": "my-design-system",  "private": true,  "scripts": {  "dev": "turbo run dev",  "build": "turbo run build",  "test": "turbo run test",  "lint": "turbo run lint",  "clean": "turbo run clean"  },  "devDependencies": {  "turbo": "^2.3.1",  "typescript": "^5.7.2",  "tsup": "^8.3.5",  "vitest": "^2.1.6",  "prettier": "^3.4.1"  },  "packageManager": "[email protected]",  "engines": {  "node": ">=18.0.0"  } }

Enter fullscreen mode

Exit fullscreen mode

โ˜… "private": true is a critical setting. It prevents the root package from being accidentally published to NPM.

โ˜… The "packageManager" field ensures that everyone who clones the project uses the same pnpm version. Corepack reads this field and automatically activates the correct package manager.

โ˜… Shared devDependencies (like TypeScript and Prettier) are installed at the root, so individual packages don't need to install them separately.

Step 4: Create the Project Structure

Now let's create the folder structure:

mkdir -p packages/core mkdir -p packages/utils mkdir -p packages/theme mkdir -p apps/docs

Enter fullscreen mode

Exit fullscreen mode

A typical monorepo structure looks like this:

my-design-system/ โ”œโ”€โ”€ packages/ โ”‚ โ”œโ”€โ”€ core/ # Core library โ”‚ โ”œโ”€โ”€ utils/ # Shared utility functions โ”‚ โ””โ”€โ”€ theme/ # Theme and style definitions โ”œโ”€โ”€ apps/ โ”‚ โ””โ”€โ”€ docs/ # Documentation app โ”œโ”€โ”€ .changeset/ # Changesets configuration โ”œโ”€โ”€ .github/ # CI/CD workflows โ”œโ”€โ”€ package.json # Root workspace config โ”œโ”€โ”€ pnpm-workspace.yaml # Workspace definition โ”œโ”€โ”€ turbo.json # Turborepo pipeline โ”œโ”€โ”€ tsconfig.base.json # Shared TypeScript config โ””โ”€โ”€ pnpm-lock.yaml # Lock file

Enter fullscreen mode

Exit fullscreen mode

โ˜… Libraries and shared modules live under packages/. Each one can be published as an independent npm package.

โ˜… Applications that consume these packages go under apps/.

โ˜… Configuration files at the root apply across the entire monorepo.

You also need to initialize each sub-package. For example, for packages/core:

cd packages/core pnpm init

Enter fullscreen mode

Exit fullscreen mode

This creates a package.json for each package. Using namespaced package names is a good practice:

{  "name": "@myds/core",  "version": "0.1.0",  "main": "dist/index.js",  "types": "dist/index.d.ts",  "scripts": {  "build": "tsup src/index.ts --format cjs,esm --dts",  "dev": "tsup src/index.ts --format cjs,esm --dts --watch"  } }

Enter fullscreen mode

Exit fullscreen mode

โ˜… Scoped names like @myds/core prevent namespace collisions on NPM.

โ˜… The main and types fields ensure that other packages resolve the correct files when importing this package.

โ˜… tsup is a great tool for quickly bundling TypeScript packages. It produces output in both CJS and ESM formats.

๐Ÿ”— Cross-Package Dependency Management with the workspace Protocol

One of the most powerful features of a monorepo is the ability for packages to reference each other locally. pnpm achieves this through the workspace: protocol.

Let's make this concrete with an example. Say the @myds/theme package depends on @myds/core. You'd define this dependency like so:

{  "name": "@myds/theme",  "version": "0.4.0",  "dependencies": {  "@myds/core": "workspace:*"  } }
*

Enter fullscreen mode

Exit fullscreen mode

โ˜… The workspace:* expression tells pnpm: "Don't download this package from the npm registry โ€” use the local version from the workspace." โ˜… During development, packages are linked together via symlinks. Any change you make in core is instantly reflected in theme.*

But what happens at publish time? Here's the beauty of pnpm: when you run pnpm publish, workspace:* is automatically converted to the actual version number.*

So while package.json looks like this during development:

{  "dependencies": {  "@myds/core": "workspace:*"  } }
*

Enter fullscreen mode

Exit fullscreen mode

It becomes this when published to NPM:

{  "dependencies": {  "@myds/core": "0.4.0"  } }

Enter fullscreen mode

Exit fullscreen mode

Variants of the workspace Protocol

pnpm offers several workspace protocol variants:

โ˜… workspace:* is the most commonly used variant. It means "use whatever version is currently in the workspace." At publish time, it resolves to an exact version (e.g., "0.4.0").*

โ˜… workspace:^ resolves to a caret range at publish time (e.g., "^0.4.0"). This allows minor and patch updates.

โ˜… workspace:~ resolves to a tilde range at publish time (e.g., "~0.4.0"). This allows only patch updates.

Here's my take on when to use which: if you always publish all packages together, workspace:* is sufficient. But if you want consumers to be able to mix different versions, workspace:^ offers more flexibility.*

Adding Dependencies Between Packages

To add one workspace package as a dependency of another, use the --filter flag:

pnpm add @myds/core --filter @myds/theme

Enter fullscreen mode

Exit fullscreen mode

โ˜… The --filter flag runs the command only in the specified package.

โ˜… pnpm automatically adds the dependency as workspace:^ (if it exists in the workspace).

You can also run scripts across all dependents of a specific package:

# Build @myds/core and all packages that depend on it pnpm --filter "@myds/core..." build

Enter fullscreen mode

Exit fullscreen mode

To add a dependency to the root workspace, use the --workspace-root (or -w for short) flag:

pnpm add -D typescript --workspace-root

Enter fullscreen mode

Exit fullscreen mode

โšก Build Orchestration with Turborepo

While pnpm workspace alone is sufficient for package management and linking, build processes get more complex as the number of packages grows. Questions like "Which package should be built first?" and "Do we really need to rebuild packages that haven't changed?" start to come up.

This is exactly where Turborepo steps in. Turborepo is a build system developed by Vercel that works directly with pnpm workspace. It reads pnpm's workspace structure, analyzes the dependency graph between packages, and optimizes tasks accordingly. It has two core features:

โ˜… Smart task scheduling: It analyzes the dependency graph between packages and automatically determines the build order. Packages that don't depend on each other are run in parallel.

โ˜… Hard caching: If a package hasn't changed, it restores the previous build output from cache. While the first build might take 30 seconds, a cached build can complete in 0.2 seconds.

Let's Set Up Turborepo

Add turbo to the root package.json:

pnpm add -D turbo --workspace-root

Enter fullscreen mode

Exit fullscreen mode

โ˜… The --workspace-root flag (or -w for short) installs the package at the root workspace, making turbo available across the entire monorepo.

turbo.json Configuration

Create a turbo.json file at the project root:

{  "$schema": "https://turbo.build/schema.json",  "tasks": {  "build": {  "dependsOn": ["^build"],  "outputs": ["dist/**"]  },  "dev": {  "cache": false,  "persistent": true  },  "test": {  "dependsOn": ["build"],  "outputs": []  },  "test:watch": {  "cache": false,  "persistent": true  },  "lint": {  "outputs": []  },  "clean": {  "cache": false  },  "check-types": {  "dependsOn": ["^build"],  "outputs": []  }  } }
**

Enter fullscreen mode

Exit fullscreen mode

Let's pause here and understand something important: the ^ in "dependsOn": ["^build"] is crucial. It means "run my dependencies' build task first, then build me." So before @myds/theme is built, its dependency @myds/core gets built first.

โ˜… "outputs": ["dist/"] tells Turborepo which files to cache. On the next build, if the source code hasn't changed, the dist/ folder is restored from cache.

โ˜… "cache": false disables caching for certain tasks. Caching doesn't make sense for long-running (persistent) tasks like dev servers.

โ˜… "persistent": true keeps the task running in the background. This is necessary for scenarios like dev servers and watch mode.

Now you can build all packages with a single command:

pnpm build

This runs turbo run build

Turborepo analyzes the dependency graph

Builds in order: first core, then theme, then utils...

Restores unchanged packages from cache`

Enter fullscreen mode

Exit fullscreen mode

๐Ÿฆ‹ Version Management with Changesets

One of the trickiest aspects of a monorepo is version management. You have multiple packages, each potentially with its own independent version number. How do you decide which one to bump and when, or how to generate the changelog?

This is exactly where Changesets comes in. Changesets is a versioning tool that lets you record each change as a small markdown file.

Step 1: Install Changesets

pnpm add -D @changesets/cli --workspace-root pnpm changeset init

Enter fullscreen mode

Exit fullscreen mode

โ˜… The changeset init command creates a .changeset/ directory in your project. โ˜… Inside this directory, you'll find a config.json and a README.md file.

Step 2: Configure Changesets

Configure the .changeset/config.json file for your project:

{  "$schema": "https://unpkg.com/@changesets/[email protected]/schema.json",  "changelog": [  "@changesets/changelog-github",  { "repo": "your-org/your-monorepo" }  ],  "commit": false,  "fixed": [],  "linked": [["@myds/*"]],  "access": "public",  "baseBranch": "master",  "updateInternalDependencies": "patch",  "ignore": [] }
*

Enter fullscreen mode

Exit fullscreen mode

Let's break down each of these settings:

โ˜… In the changelog section, we're using @changesets/changelog-github. This automatically adds PR links and contributor information to the changelog. Let's install this package as well:

pnpm add -D @changesets/changelog-github --workspace-root

Enter fullscreen mode

Exit fullscreen mode

โ˜… The linked field is very important. [["@myds/"]] tells Changesets that the versions of all packages under the @myds scope are linked together. If one gets a major bump, they all do.

โ˜… "access": "public" ensures that scoped packages (like @myds/core) are published as public on NPM. Without this setting, scoped packages default to private and will throw an error during publish.

โ˜… "updateInternalDependencies": "patch" automatically applies a patch bump to the internal dependency versions of packages that depend on an updated package.

โ˜… "baseBranch": "master" determines which branch Changesets uses as its baseline. Depending on your project, this could be main or master.

Step 3: Create a Changeset

When you make a change, create a changeset file before opening a PR:

pnpm changeset

Enter fullscreen mode

Exit fullscreen mode

This launches an interactive wizard:

๐Ÿฆ‹ Which packages would you like to include?  โ—ฏ @myds/core  โ—ฏ @myds/theme  โ—ฏ @myds/utils

๐Ÿฆ‹ Which packages should have a major bump? ๐Ÿฆ‹ Which packages should have a minor bump?

๐Ÿฆ‹ Summary: Added dark mode support to the core package`

Enter fullscreen mode

Exit fullscreen mode

This wizard creates a randomly named markdown file in the .changeset/ directory:

--- "@myds/core": minor "@myds/theme": patch ---

Added dark mode support to the core package. Updated related color variables in the theme package.`

Enter fullscreen mode

Exit fullscreen mode

โ˜… The frontmatter section specifies which package gets bumped at which level. โ˜… The text below becomes the description added to the changelog. โ˜… This file is included in the commit and submitted for review along with the PR.

Step 4: Apply the Versions

Once all changesets have been collected, apply the versions:

pnpm changeset version

Enter fullscreen mode

Exit fullscreen mode

โ˜… This command reads all changeset files. โ˜… Updates the version numbers in the relevant packages' package.json files. โ˜… Automatically creates/updates each package's CHANGELOG.md. โ˜… Deletes the consumed changeset files.

Step 5: Publish

After versions have been applied, publish:

pnpm changeset publish

Enter fullscreen mode

Exit fullscreen mode

This publishes all changed packages to the NPM registry. But are you going to do this manually every time? Of course not! In the next section, we'll automate this entire process ๐Ÿ˜

๐Ÿš€ Automated NPM Publishing with GitHub Actions

Enough theory โ€” time to get our hands dirty! ๐Ÿ› ๏ธ The real power of Changesets shines when combined with GitHub Actions. Let's build a CI/CD pipeline step by step.

Step 1: Create an NPM Token

First, you need to create an Automation Token on NPM:

โ˜… Go to your profile settings on npmjs.com.

โ˜… In the Access Tokens section, select Generate New Token.

โ˜… Set the token type to Automation. This type allows publishing without requiring 2FA.

โ˜… Copy the generated token (it's only shown once!).

Step 2: Add Tokens to GitHub Secrets

In your GitHub repo, go to Settings > Secrets and variables > Actions and add the following secret:

โ˜… NPM_TOKEN: The NPM automation token you just created.

GITHUB_TOKEN is automatically provided by GitHub for every workflow run, so you don't need to set it up manually.

Step 3: Configure Repository Permissions

In your GitHub repo, go to Settings > Actions > General:

โ˜… Under Workflow permissions, enable the "Read and write permissions" option.

โ˜… Check the "Allow GitHub Actions to create and approve pull requests" checkbox.

Without these settings, the Changesets bot won't be able to create PRs.

Step 4: Create the Release Workflow File

Now create the .github/workflows/release.yml file:

name: Release

on: push: branches:

  • master

concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: false

permissions: contents: write pull-requests: write packages: write id-token: write

jobs: release: name: Release runs-on: ubuntu-latest steps:

  • name: Checkout Repo uses: actions/checkout@v4 with: fetch-depth: 0

  • name: Setup pnpm uses: pnpm/action-setup@v4

  • name: Setup Node.js uses: actions/setup-node@v4 with: node-version: 20 cache: "pnpm" registry-url: "https://registry.npmjs.org"

  • name: Install Dependencies run: pnpm install --frozen-lockfile

  • name: Build All Packages run: pnpm build

  • name: Create Release Pull Request or Publish id: changesets uses: changesets/action@v1 with: commit: "chore: update versions" title: "chore: update versions" publish: pnpm run ci:publish env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} NPM_TOKEN: ${{ secrets.NPM_TOKEN }} NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}`

Enter fullscreen mode

Exit fullscreen mode

There's a lot going on in this workflow. Let's break it down piece by piece:

โ˜… The concurrency setting prevents multiple release workflows from running simultaneously. If a new push comes in while a publish is in progress, the old workflow isn't cancelled โ€” instead, the new one waits in the queue.

โ˜… The permissions block gives the workflow the authority to write to the repo, create PRs, and publish packages.

โ˜… fetch-depth: 0 pulls the full git history. Changesets needs the commit history to calculate versions.

โ˜… pnpm/action-setup@v4 installs pnpm in the CI environment. It reads the packageManager field from package.json to install the correct version.

โ˜… --frozen-lockfile is a critical flag for CI. It installs the exact same dependencies as specified in the lock file. If pnpm-lock.yaml is out of date, it throws an error.

โ˜… changesets/action@v1 is the action that does all the heavy lifting. It handles two different scenarios:

Scenario A: If there are pending changeset files in the .changeset/ directory, it creates or updates a "Version Packages" PR. This PR contains the version bumps and changelog updates.

Scenario B: If there are no pending changesets (i.e., after the Version Packages PR has been merged), it runs the publish command to publish the packages to NPM.

Step 5: Define the ci:publish Script

Add the publish script to the root package.json:

{  "scripts": {  "ci:publish": "pnpm publish -r --access public"  } }

Enter fullscreen mode

Exit fullscreen mode

โ˜… The -r flag (recursive) publishes all workspace packages. โ˜… --access public ensures that scoped packages are published as public.

Changeset Bot

Optionally, you can install the Changeset Bot from github.com/apps/changeset-bot on your repo. This bot checks whether PRs include a changeset file and leaves a warning comment if they don't.

๐ŸŽฏ Demo: The Tuvix.js Monorepo Project

If you'd like to see the entire architecture described in this article in action, check out the Tuvix.js project I've been developing. Tuvix.js is a lightweight micro frontend framework that uses exactly the stack covered in this article:

โ˜… pnpm workspace managing 14+ packages in a single repo

โ˜… Turborepo for build orchestration and caching

โ˜… Changesets for version management and automated NPM publishing

โ˜… GitHub Actions for the CI/CD pipeline

You can explore the pnpm-workspace.yaml, turbo.json, .changeset/config.json, and GitHub Actions workflow files directly in the repository.

Check out the repo here ๐Ÿ‘‡

github.com/yasinatesim/tuvix.js

๐Ÿ“ฌ Feedback

While writing this article, I used my own notes for identifying sources and research, the Claude Opus 4.6 model for proofreading and additional research, and the Gemini 3 Pro Preview 2k (Nano Banana Pro) model for generating images.

I welcome any advice, suggestions, or feedback on this article. If you'd like to get in touch, you can reach me through my social media links on my website or via LinkedIn.

Best, Yasin ๐Ÿค—

๐Ÿ“š Resources Used While Writing This Article

  • pnpm Workspaces โ€” Official documentation for pnpm's workspace feature

  • Using Changesets with pnpm โ€” Guide on Changesets integration with pnpm

  • Turborepo - Structuring a Repository โ€” Turborepo's monorepo structuring documentation

  • Changesets GitHub Action โ€” Official Changesets GitHub Action repository and usage guide

  • Tuvix.js GitHub Repository โ€” My micro frontend framework, this is monorepo project referenced in this article

Was this article helpful?

Sign in to highlight and annotate this article

AI
Ask AI about this article
Powered by AI News Hub ยท full article context loaded
Ready

Conversation starters

Ask anything about this articleโ€ฆ

Daily AI Digest

Get the top 5 AI stories delivered to your inbox every morning.

More about

claudegeminimodel

Knowledge Map

Knowledge Map
TopicsEntitiesSource
Monorepo Arโ€ฆclaudegeminimodelreleaselaunchavailableDEV Communiโ€ฆ

Connected Articles โ€” Knowledge Graph

This article is connected to other articles through shared AI topics and tags.

Knowledge Graph100 articles ยท 227 connections
Scroll to zoom ยท drag to pan ยท click to open

Discussion

Sign in to join the discussion

No comments yet โ€” be the first to share your thoughts!

More in Releases

็ผ“ๅญ˜ๆžถๆž„ๆทฑๅบฆๆŒ‡ๅ—๏ผšๅฆ‚ไฝ•่ฎพ่ฎก้ซ˜ๆ€ง่ƒฝ็ผ“ๅญ˜็ณป็ปŸ
ReleasesFresh

็ผ“ๅญ˜ๆžถๆž„ๆทฑๅบฆๆŒ‡ๅ—๏ผšๅฆ‚ไฝ•่ฎพ่ฎก้ซ˜ๆ€ง่ƒฝ็ผ“ๅญ˜็ณป็ปŸ

<h1> ็ผ“ๅญ˜ๆžถๆž„ๆทฑๅบฆๆŒ‡ๅ—๏ผšๅฆ‚ไฝ•่ฎพ่ฎก้ซ˜ๆ€ง่ƒฝ็ผ“ๅญ˜็ณป็ปŸ </h1> <blockquote> <p>ๅœจ็Žฐไปฃๅˆ†ๅธƒๅผ็ณป็ปŸไธญ๏ผŒ็ผ“ๅญ˜ๆ˜ฏๆๅ‡็ณป็ปŸๆ€ง่ƒฝ็š„ๆ ธๅฟƒ็ป„ไปถใ€‚ๆœฌๆ–‡ๅฐ†ๆทฑๅ…ฅๆŽข่ฎจ็ผ“ๅญ˜ๆžถๆž„็š„่ฎพ่ฎกๅŽŸๅˆ™ใ€็ญ–็•ฅไธŽๅฎžๆˆ˜ๆŠ€ๅทงใ€‚</p> </blockquote> <h2> ไธบไป€ไนˆ่ฆไฝฟ็”จ็ผ“ๅญ˜๏ผŸ </h2> <p>ๅœจ่ฝฏไปถ็ณป็ปŸไธญ๏ผŒ็ผ“ๅญ˜็š„ๆœฌ่ดจๆ˜ฏ<strong>็”จ็ฉบ้—ดๆขๆ—ถ้—ด</strong>ใ€‚้€š่ฟ‡ๅฐ†้ข‘็น่ฎฟ้—ฎ็š„ๆ•ฐๆฎๅญ˜ๅ‚จๅœจ้ซ˜้€Ÿๅญ˜ๅ‚จไป‹่ดจไธญ๏ผŒๅ‡ๅฐ‘ๅฏนๆ…ข้€Ÿๆ•ฐๆฎๆบ็š„่ฎฟ้—ฎๆฌกๆ•ฐ๏ผŒไปŽ่€Œๆ˜พ่‘—ๆๅ‡็ณป็ปŸๅ“ๅบ”้€Ÿๅบฆใ€‚</p> <p>ๅ…ธๅž‹ๅœบๆ™ฏ๏ผš</p> <ul> <li>ๆ•ฐๆฎๅบ“ๆŸฅ่ฏข็ป“ๆžœ็ผ“ๅญ˜</li> <li>APIๅ“ๅบ”็ผ“ๅญ˜</li> <li>ไผš่ฏ็Šถๆ€็ผ“ๅญ˜</li> <li>่ฎก็ฎ—็ป“ๆžœ็ผ“ๅญ˜</li> </ul> <h2> ็ผ“ๅญ˜ๆžถๆž„่ฎพ่ฎกๅŽŸๅˆ™ </h2> <h3> 1. ็ผ“ๅญ˜ๅฑ‚็บง็ญ–็•ฅ </h3> <p>็Žฐไปฃ็ณป็ปŸ้€šๅธธ้‡‡็”จๅคš็บง็ผ“ๅญ˜ๆžถๆž„๏ผš<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code>โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ CDN (่พน็ผ˜็ผ“ๅญ˜) โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ Redis/Memcached โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ ๆœฌๅœฐ็ผ“ๅญ˜ โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ ๆ•ฐๆฎๅบ“ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ </code></pre> </div> <p><strong>ๅŽŸๅˆ™<