• トップ
  • ブログ一覧
  • Claude Opus 4.5とSonnet 4.5に同じ実装をお願いしてみた
  • Claude Opus 4.5とSonnet 4.5に同じ実装をお願いしてみた

    ずお(エンジニア)ずお(エンジニア)
    2026.01.09

    IT技術

    はじめに

    2025年11月25日、Anthropicから最新モデル「Claude Opus 4.5」がリリースされました。

    Opus4.5は、他のAIモデルと比較しても、現段階では最先端のモデルとなったようです。
    トークン出力時にかかるコストも従来のOpusから67%削減され、Maxプランだと、普段使いではほぼ上限に行くことはなくなり、Opusでの開発がより一層楽しめます。
    私は、会社のサブスク補助制度を利用して、Maxプラン($100)に加入させていただいています。100ドルの補助はありがたすぎます!!感謝です。

    また、Claude Codeがデスクトップアプリで利用可能になったことで、アプリ上から並列実行が可能になったり、プランモードが進化したりなど、大幅なアップグレードも加わっているようです。
    詳細はこちらから確認ができます。

    そこで今回は、Claude Opus 4.5とSonnet 4.5に全く同じプロンプトを与え、Next.js 16での無限スクロール一覧ページを実装させることで、両モデルの特性を比較検証してみようと思います。

    やったこと

    検証環境
    ・Next.js 16.0.5(App Router)
    ・React 19.2
    ・TypeScript 5.x
    ・Tailwind CSS 4.x
    検証の流れ
    1. まず共通のCLAUDE.mdを用意
    2. 両モデルに同じプロンプトで無限スクロールの実装をお願い
    3. できあがったコードを見比べてみる

    CLAUDE.mdで決めたルール
    ざっくり下記のような感じです。
    サーバーファースト: 基本はサーバーコンポーネント、クライアントは最小限に
    useEffect禁止
    境界を明確に: server-onlyなど使用してコードの役割をはっきりさせる
    新機能試してみてね

    生成されたコードを見てみる

    ファイル構成の違い

    Opus4.5の場合

    1src/
    2├── app/items/
    3│   ├── page.tsx              # Server Component
    4│   ├── actions.ts            # Server Actions
    5│   └── loading.tsx
    6├── components/features/items/
    7│   ├── infinite-scroll-container.tsx  # 統合型
    8│   ├── item-card.tsx
    9│   └── item-list.tsx
    10└── lib/server/items.ts       # server-only

    Sonnet4.5の場合

    1src/
    2├── app/items/
    3│   ├── page.tsx
    4│   ├── actions.ts
    5│   └── loading.tsx
    6├── components/
    7│   ├── items-list.tsx
    8│   ├── infinite-scroll-trigger.tsx   # 独立コンポーネント
    9│   ├── item-card.tsx
    10│   └── item-card-skeleton.tsx
    11└── lib/server/items.ts

    面白いところが、SonnetがInfiniteScrollTriggerを独立したコンポーネントにしているところです。
    実はCLAUDE.mdファイルに無限スクロールの実装例を書いていたのですが、その設計とそっくりそのままでした。逆にOpus4.5はそんな実装ではダメだと判断したのか、統合型のコンテナを作ってきました。

    状態管理のアプローチ
    Opus4.5ではuseReducerとuseTransitionを使用した実装となっていました。

    1// infinite-scroll-container.tsx
    2'use client'
    3
    4import { useCallback, useTransition, useReducer } from 'react'
    5
    6type State = {
    7  items: Item[]
    8  hasMore: boolean
    9  page: number
    10}
    11
    12type Action = {
    13  type: 'LOAD_MORE'
    14  payload: { items: Item[]; hasMore: boolean }
    15}
    16
    17function reducer(state: State, action: Action): State {
    18  switch (action.type) {
    19    case 'LOAD_MORE':
    20      return {
    21        items: [...state.items, ...action.payload.items],
    22        hasMore: action.payload.hasMore,
    23        page: state.page + 1,
    24      }
    25    default:
    26      return state
    27  }
    28}
    29
    30export function InfiniteScrollContainer({
    31  initialItems,
    32  initialHasMore,
    33  fetchMoreAction,
    34}: InfiniteScrollContainerProps) {
    35  const [isPending, startTransition] = useTransition()
    36  const [state, dispatch] = useReducer(reducer, {
    37    items: initialItems,
    38    hasMore: initialHasMore,
    39    page: 1,
    40  })
    41
    42  const loadMore = useCallback(() => {
    43    if (isPending || !state.hasMore) return
    44
    45    startTransition(async () => {
    46      const nextPage = state.page + 1
    47      const result = await fetchMoreAction(nextPage)
    48      dispatch({
    49        type: 'LOAD_MORE',
    50        payload: { items: result.items, hasMore: result.hasMore },
    51      })
    52    })
    53  }, [isPending, state.hasMore, state.page, fetchMoreAction])
    54
    55  // ...
    56}

    対するSonnetはオーソドックスなuseStateで実装

    1// items-list.tsx
    2'use client'
    3
    4import { useState } from 'react'
    5
    6export function ItemsList({
    7  initialItems,
    8  initialHasMore,
    9}: ItemsListProps) {
    10  const [items, setItems] = useState(initialItems)
    11  const [hasMore, setHasMore] = useState(initialHasMore)
    12  const [page, setPage] = useState(1)
    13  const [isLoading, setIsLoading] = useState(false)
    14
    15  const loadMore = async () => {
    16    if (isLoading || !hasMore) return
    17
    18    setIsLoading(true)
    19    try {
    20      const nextPage = page + 1
    21      const result = await loadMoreItems(nextPage)
    22      setItems(prev => [...prev, ...result.items])
    23      setHasMore(result.hasMore)
    24      setPage(nextPage)
    25    } catch (error) {
    26      console.error('Failed to load more items:', error)
    27    } finally {
    28      setIsLoading(false)
    29    }
    30  }
    31
    32  // ...
    33}
    34
    35}

    CLAUDE.mdファイルのルール、どれくらい守っているか
    両モデルとも一定のルールは守っている印象でしたが、ところどころで、性格の違いが出ているようです。
    私なりにまとめると、
    Opus 4.5は「実践派」
    Opusのコードを見ていると、こんな特徴が見えてきました。
    ・useReducerで状態をしっかり管理: 複雑になっても追いやすい(将来的なことを見据えた実装)
    ・useTransitionでUXを向上: ローディング中も画面が固まらない
    ・ロジックは1箇所にまとめる: 分散させずに把握しやすくする
    「現場で動くコード」を意識している感じがしました。表示された一覧ページや無限ローディングの挙動などからしてもOpus4.5の方がUXは間違いないです。

    Sonnet 4.5は「教科書派」
    一方、Sonnetは下記のような感じでした
    ・コンポーネントをきれいに分離: 再利用性を重視
    ・useStateでシンプルに: 誰でも読める
    ・ガイドラインの例に忠実: 言われたとおりに作る
    チュートリアルに載っていそうなコードの印象でした。ただSonnetで出された成果物は、色々なエラーが発生していたのでチューニングが必要でした。

    生成速度について
    こちらはOpus4.5が圧倒的に速かったです。
    体感ですが(測っておけばよかった、、)、Sonnetの1/10くらいの速さで実装が終わったので、かなり驚きです。従来のOpusも結構遅かったので、この改善はとても嬉しいです。

    最後に

    今回は単純なページ実装だったので、そこまでの差がつかなかったかもしれないですが、お二人の性格が全然違うんだなということが理解できたのでよかったです。
    ただ、私もまだOpus4.5を使用してそんなに日が経っていないですが、触っている感じだと、デバッグ、処理速度、ドキュメント生成などの能力は格段に上がっていると感じます。
    例えば、「〇〇のエンドポイントの~~の処理を修正する場合の影響範囲を洗い出して、必要な作業をエピックとチケットに分解してください。」と簡潔に指示するだけで、かなり精度の高いものが作られます。(Notion MCPなどと連携させておけば、効率UPです)
    docsディレクトリなどをclaude用に配置しておき、claude.mdファイルに作業時はdocsディレクトリを確認するようにと書いておくと、さらに精度が増す所感でした。

    今後もClaudeで色々なことを試してみて、開発速度の向上に役立てたり、壁打ち相手として活用しながら自分のスキルも高めていきたいと思います。

    ライトコードでは、エンジニアを積極採用中!

    ライトコードでは、エンジニアを積極採用しています!カジュアル面談等もございますので、くわしくは採用情報をご確認ください。

    採用情報へ

    ずお(エンジニア)
    ずお(エンジニア)
    Show more...

    おすすめ記事

    エンジニア大募集中!

    ライトコードでは、エンジニアを積極採用中です。

    特に、WEBエンジニアとモバイルエンジニアは是非ご応募お待ちしております!

    また、フリーランスエンジニア様も大募集中です。

    background