JetBrains MPS(Meta Programming System)は、DSL(Domain-Specific Language)を設計・実装するための強力なツールである。しかし、既存プロジェクトにMPSを導入するのは、技術的にも組織的にも困難が伴う。本記事では、10年以上のMPS経験から得た、段階的な導入方法を解説する。
なぜMPSを導入するのか
MPSのメリット
従来の開発:
- コードの重複が多い
- ビジネスルールがコードに埋もれる
- ドメインエキスパートがコードを読めない
MPS導入後:
- DSLでビジネスルールを表現
- コード生成で一貫性を保証
- ドメインエキスパートが直接編集可能導入が適している場合
適している:
- ボイラープレートコードが多い
- ドメインルールが複雑
- コード生成に価値がある
- 長期的なメンテナンスが必要
適していない:
- 単純なCRUD
- 短期プロジェクト
- チームが小さい(3人以下)
- 学習コストを払えない導入の原則
1. 段階的導入
❌ いきなり全体をMPS化
✅ 小さな領域から段階的に導入2. チームの合意
❌ トップダウンで押し付け
✅ ボトムアップで合意形成3. リスク最小化
❌ 本番コードに直接適用
✅ テストプロジェクトで検証導入ステップ
Phase 1: 調査・評価(1-2週間)
1.1 ボトルネックの特定
プロジェクトの課題を洗い出す:
- コードの重複が多い箇所
- 手動で書くのが面倒な箇所
- ドメインルールが複雑な箇所例: REST APIのボイラープレート
// 手動で書くと...
app.get('/users', async (req, res) => {
try {
const users = await db.query('SELECT * FROM users')
res.json(users)
} catch (error) {
res.status(500).json({ error: error.message })
}
})
app.get('/posts', async (req, res) => {
try {
const posts = await db.query('SELECT * FROM posts')
res.json(posts)
} catch (error) {
res.status(500).json({ error: error.message })
}
})
// → 重複が多い!1.2 MPS適用範囲の決定
候補:
- REST API定義
- データバリデーション
- ビジネスルール
- 設定ファイル生成
- テストコード生成
選択基準:
- 重複が多い
- 変更頻度が高い
- ドメインエキスパートが関与する1.3 PoC(Proof of Concept)
小さなプロトタイプを作成し、効果を検証する。
PoC目標:
- MPSで簡単なDSLを作成
- コード生成を試す
- 生成コードの品質を確認
- 学習コストを評価Phase 2: パイロットプロジェクト(2-4週間)
2.1 小さな機能をMPS化
例: REST APIのDSL化
REST API DSL:
Resource User {
GET /users -> List<User>
GET /users/:id -> User
POST /users -> User
PUT /users/:id -> User
DELETE /users/:id -> void
}
Resource Post {
GET /posts -> List<Post>
GET /posts/:id -> Post
}生成されるコード:
// 自動生成: src/routes/users.ts
import { Router } from 'express'
import { UserRepository } from '../repositories/UserRepository'
export const userRouter = Router()
userRouter.get('/users', async (req, res) => {
try {
const users = await UserRepository.findAll()
res.json(users)
} catch (error) {
res.status(500).json({ error: error.message })
}
})
userRouter.get('/users/:id', async (req, res) => {
try {
const user = await UserRepository.findById(req.params.id)
res.json(user)
} catch (error) {
res.status(500).json({ error: error.message })
}
})
// ... 他のエンドポイントも自動生成2.2 既存コードとの統合
統合方法:
1. MPSで生成されたコードを別ディレクトリに出力
2. 既存プロジェクトからimport
3. 既存のテストで検証
4. 問題なければ既存コードを削除ディレクトリ構造:
project/
├── src/
│ ├── generated/ # MPS生成コード
│ │ └── routes/
│ ├── manual/ # 手書きコード
│ └── index.ts # エントリーポイント
├── mps-project/ # MPSプロジェクト
│ └── solutions/
│ └── api-dsl/
└── package.json2.3 チームでの検証
検証項目:
- 生成コードの品質
- 開発速度の変化
- エラー率の変化
- 学習コストの妥当性
- チームの反応Phase 3: 段階的拡大(1-3ヶ月)
3.1 適用範囲を拡大
順次適用:
1. REST API全体
2. データバリデーション
3. ビジネスルール
4. テストコード生成3.2 ドキュメント整備
# MPSガイド
## DSLの使い方
- 新しいResourceの追加方法
- エンドポイントの定義方法
- バリデーションの追加方法
## コード生成
- 生成コマンド
- 出力先の確認
- デバッグ方法
## トラブルシューティング
- よくあるエラー
- 解決方法3.3 CI/CDへの統合
# .github/workflows/mps-build.yml
name: MPS Build
on: [push, pull_request]
jobs:
generate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Setup MPS
run: |
wget https://download.jetbrains.com/mps/...
tar -xzf mps-*.tar.gz
- name: Generate Code
run: |
./mps/bin/mps.sh --script generate.mps
- name: Commit Generated Code
run: |
git config user.name "MPS Bot"
git add src/generated/
git commit -m "Generated code from MPS"
git pushPhase 4: 本格運用(継続)
4.1 チーム教育
教育プログラム:
- MPSの基礎(2時間)
- DSLの使い方(1時間)
- 実践ワークショップ(2時間)
- Q&Aセッション(1時間)4.2 継続的改善
改善サイクル:
- DSLの使いにくい点を収集
- ジェネレータの最適化
- エラーメッセージの改善
- エディタ支援の強化4.3 メトリクスの測定
測定指標:
- コード行数の削減
- 開発時間の短縮
- バグ発生率の変化
- チームの満足度実例: REST API DSLの導入
Before(MPS導入前)
// 20ファイル、合計2000行のコード
// 各エンドポイントごとに同じパターンを手書き
// src/routes/users.ts (100行)
// src/routes/posts.ts (100行)
// src/routes/comments.ts (100行)
// ...
手動メンテナンス:
- エラーハンドリングの統一が困難
- 新しいエンドポイント追加に30分
- レビューで見逃されるバグが多いAfter(MPS導入後)
// MPS DSL: 20リソース、合計200行の定義
Resource User { ... }
Resource Post { ... }
Resource Comment { ... }
自動生成:
- 2000行のTypeScriptコードを生成
- エラーハンドリングが統一
- 新しいエンドポイント追加に5分
- 型安全性が保証される
メトリクス:
- 開発時間: 83%削減
- コードレビュー時間: 90%削減
- バグ発生率: 70%削減よくある失敗パターン
1. いきなり全体をMPS化
❌ 失敗例:
- 既存コード全体をMPS化しようとする
- チームがついていけない
- プロジェクトが停滞
✅ 成功例:
- 小さな領域から段階的に導入
- チームが慣れてから拡大2. 学習コストを軽視
❌ 失敗例:
- MPSの学習に時間を割かない
- チームが使い方を理解していない
- 結局使われなくなる
✅ 成功例:
- 十分な教育時間を確保
- ハンズオンで実践
- ドキュメントを整備3. ドキュメント不足
❌ 失敗例:
- DSLの使い方が不明
- エラーメッセージが不親切
- チームが困惑
✅ 成功例:
- 詳細なドキュメント
- サンプルコードを用意
- FAQ を整備チーム合意の取り方
1. メリットを明確に示す
数値で示す:
- コード削減: 90%削減
- 開発時間: 80%短縮
- バグ削減: 70%削減
デモを見せる:
- MPSでDSLを編集
- コード生成を実演
- 生成されたコードを確認2. リスクを最小化
安全策:
- 小さな範囲から開始
- いつでも元に戻せる
- 既存コードと並行運用3. チームの意見を聞く
フィードバック収集:
- 週次ミーティング
- レトロスペクティブ
- 改善提案の収集トラブルシューティング
問題1: MPSが重い
解決策:
- メモリを増やす(-Xmx4096m)
- 不要なプラグインを無効化
- インデックスを再構築問題2: 生成コードにバグ
解決策:
- ジェネレータのテストを充実
- 生成コードのレビュー
- デバッグログの追加問題3: チームが使えない
解決策:
- ペアプログラミング
- ハンズオン研修
- メンターを配置まとめ
JetBrains MPSを既存プロジェクトに導入する際の原則:
- 段階的導入: 小さな領域から始める
- チーム合意: ボトムアップで進める
- リスク最小化: いつでも戻せる状態を保つ
- 教育投資: 十分な学習時間を確保
- 継続的改善: フィードバックを元に改善
導入ステップ:
- Phase 1: 調査・評価(1-2週間)
- Phase 2: パイロットプロジェクト(2-4週間)
- Phase 3: 段階的拡大(1-3ヶ月)
- Phase 4: 本格運用(継続)
成功の鍵:
- 明確なメリットを示す
- 小さく始めて徐々に拡大
- チームの合意を得る
- 十分な教育を行う
MPSは強力なツールだが、導入には時間と労力がかかる。焦らず、段階的に進めることが成功の鍵である。