mybook

部門横断コラボレーション — エンジニアリング、セールス、プロダクトの交差点

四つの顔を持つ仕事

「ソウタ、FDE って結局何屋なのか、聞かれたらどう答える?」

月曜朝のオンライン 1on1。カイの問いは唐突だった。

ソウタは少し考えた。「エンジニアです、と答えると思います。でも最近、それだけじゃ説明がつかないことが増えました」

「先週だけでも、セールスの商談にデモ要員として同席して、プロダクトチームに顧客の要望を伝えて、CS チームのオンボーディング資料をレビューして、エンジニアリングチームにバグレポートを出しました。全部同じ日に」

カイは笑った。「それが FDE だ。エンジニアでもあり、PM でもあり、データアナリストでもあり、カスタマーサクセスでもある。四つの顔を持つハイブリッドロールだ」

Loading diagram...

INFO

FDE のユニークさは、単に複数のスキルを持つことではない。複数部門の交差点に常に立ち、情報の翻訳者として機能することにある。エンジニアの言葉をセールスに、顧客の痛みをプロダクトに、ビジネスの優先度をエンジニアリングに——この翻訳能力こそが FDE の本質的な価値だ。

「でも、四つの顔を使い分けるのは正直大変です」ソウタは率直に言った。

「使い分けるんじゃない。同時に全部であり続けるんだ。それが部門横断コラボレーションの本質だよ」

セールスチームとの協働

技術デモとPoC構築

翌週、セールスの山田から Slack が飛んできた。

「ソウタさん、来週の大型案件のデモに同席してもらえませんか? 先方の CTO が技術的に深い質問をしてくるタイプで……」

ソウタはカイに相談した。

「セールス同行は FDE の重要な仕事だ。ただし、ただの技術要員じゃない。デモの設計段階から入れ。先方の課題を理解して、そこに刺さるシナリオを組む。汎用デモを見せるのは最悪だ」

ソウタは山田と事前ミーティングを行い、先方の課題を整理した。

「先方は EC サイトの商品推薦を改善したいそうです。現状の推薦精度が低く、CTR が 2% を切っているとか」

「なら、先方のデータに近いサンプルで PoC を組め。汎用データのデモより 10 倍刺さる」ソウタは 3 日で PoC を構築した。

# app/services/demo_poc_builder.rb
class DemoPocBuilder
  def initialize(prospect_profile)
    @profile = prospect_profile
    @client = ArclightAi::Client.new
  end
 
  def build_recommendation_demo
    # 先方の業種に近いサンプルデータで推薦エンジンを構築
    sample_data = generate_industry_sample(@profile.industry)
    
    model = @client.train_recommendation_model(
      data: sample_data,
      config: {
        algorithm: "collaborative_filtering",
        features: @profile.priority_features
      }
    )
 
    {
      model_id: model.id,
      sample_predictions: model.predict(sample_data.test_set),
      estimated_ctr_improvement: model.estimated_lift,
      integration_steps: build_integration_guide
    }
  end
 
  private
 
  def generate_industry_sample(industry)
    # 業種に応じたリアルなサンプルデータを生成
    ArclightAi::SampleGenerator.create(
      industry: industry,
      record_count: 10_000,
      features: %w[user_history item_attributes context]
    )
  end
 
  def build_integration_guide
    # 先方のスタックに合わせた導入ガイドを自動生成
    ArclightAi::IntegrationGuide.generate(
      stack: @profile.tech_stack,
      deployment: @profile.deployment_type
    )
  end
end

RFP/RFI への技術対応

デモは成功し、先方から RFP(提案依頼書)が届いた。

「RFP の技術要件セクション、30 項目あります」山田が言った。「任せてください。ただ、嘘は書きません」ソウタは釘を刺した。

WARNING

RFP/RFI 対応で最も重要なのは誠実さだ。「できます」と書いて受注しても、実装フェーズで破綻すれば信頼を失う。「現時点では未対応だが、ロードマップに含まれている」「カスタム開発で対応可能(工数目安: X 人月)」のように、正確に伝えることが長期的な信頼を生む。

プロダクトチームとの協働

フィードバックの翻訳と優先度付け

「顧客のフィードバックをそのままプロダクトに投げるな」カイは言った。「翻訳しろ

「顧客は『この画面が使いにくい』と言う。でもプロダクトチームが必要としているのは、『どの画面の、どの操作で、どんなタスクを実行しようとしたときに、何秒かかって、期待との差分は何か』だ」ソウタは顧客フィードバックの伝え方を変えた。

Before(悪い例):

顧客 A 社が「ダッシュボードが遅い」と言っています。改善してほしいです。

After(良い例):

顧客 A 社(ARR ¥24M、エンタープライズプラン)から報告。ダッシュボードの「売上分析」タブで、日付範囲を 90 日以上に設定すると初回描画に 8 秒かかる。同社は月次レポートで 180 日レンジを常用。同様の報告が他 3 社(B, D, F)からあり。推定影響ユーザー数: 約 200 名。提案: クエリの集計を日次バッチに変更すれば 1 秒未満に改善可能(エンジニアリングと検証済み)。

「この差が、プロダクトの意思決定速度を変える」カイは言った。

機能リクエストの優先度付け

顧客からの要望は絶え間なく来る。全てに対応はできない。

「FDE が持つべきフレームワークがある」カイがホワイトボードに書いた。

質問スコア
影響度何社・何ユーザーに影響するか?1-5
緊急度チャーンリスクに直結するか?1-5
実現性既存アーキテクチャで対応可能か?1-5
戦略性プロダクトの方向性と合致するか?1-5

「スコアの合計で並べ替えて、上位をプロダクトに提案する。数字で語れ。感情で語るな」

エンジニアリングチームとの協働

カスタム実装と技術的制約の共有

「エンジニアリングチームとの関係が一番難しいかもしれません」ソウタは言った。「同じエンジニアだからこそ、意見がぶつかる」

「分かる」カイは頷いた。「FDE は顧客の現場にいるから『こうすべき』が見える。でもコアチームには『こうしかできない』理由がある。互いの文脈を共有することが大事だ」ソウタは学んだ。バグ報告ひとつとっても、FDE ならではの書き方がある。

# FDE が提出するバグレポートの構造化テンプレート
class FdeBugReport
  TEMPLATE = {
    customer_context: {
      company: "A社(エンタープライズ、ARR ¥24M)",
      plan: "Enterprise",
      usage_pattern: "日次バッチで10万件処理"
    },
    reproduction: {
      environment: "本番環境(us-east-1)",
      steps: [
        "売上分析タブを開く",
        "日付範囲を180日に設定",
        "集計ボタンをクリック"
      ],
      expected: "3秒以内に結果表示",
      actual: "8秒後にタイムアウトエラー"
    },
    business_impact: {
      affected_customers: 4,
      churn_risk: "中(更新3ヶ月前)",
      revenue_at_risk: "¥72M(4社合計ARR)"
    },
    technical_analysis: {
      root_cause_hypothesis: "N+1クエリ(売上テーブルのJOIN)",
      suggested_fix: "日次集計テーブルの導入",
      estimated_effort: "2-3日"
    }
  }.freeze
end

INFO

FDE のバグレポートが通常のエンジニアと違うのは、ビジネスインパクトが含まれている点だ。「このバグを直さないと ¥72M のチャーンリスクがある」——この一文がエンジニアリングチームの優先度判断を劇的に変える。

カスタマーサクセスとの協働

オンボーディング支援と活用促進

「CS チームとの連携はどう考えればいいですか?」ソウタが聞いた。

「FDE は技術的なオンボーディングの加速装置だ」カイは答えた。「CS が関係構築と活用促進を担当し、FDE が技術的なブロッカーを取り除く。この分業が重要だ」

「顧客が API 連携でつまずいているとする。CS は『お困りですね、サポートします』と言えるが、コードは書けない。FDE は実際にコードを書いて、動くサンプルを渡せる。この差が導入速度を 3 倍にする

ヘルススコアの改善

「もうひとつ。CS が追っているヘルススコアに、FDE は技術的な視点で貢献できる」

Loading diagram...

「API 利用率が低い顧客には、FDE がインテグレーションレビューを提案する。機能採用率が低ければ、ワークショップを開く。サポート件数が多ければ、根本原因を特定して解消する。技術で数字を動かすのが FDE だ」

コミュニケーションのリズム

ミーティングの設計

「部門横断で動くとき、ミーティングの設計が命だ」カイは言った。「三層構造で考えろ」

頻度形式参加者目的
日次非同期 Slack 更新FDE チーム顧客状況の共有
週次30分同期ミーティングFDE + Sales + CS案件進捗と課題
月次60分レビュー全部門リーダー戦略整合と振り返り
Loading diagram...

「日次の非同期更新が一番大事だ。Slack に決まったフォーマットで投稿する。5 分で書けるようにテンプレ化しろ」

INFO

日次更新のテンプレート例:

  • 昨日やったこと: A 社 PoC 完了、B 社バグ調査
  • 今日やること: C 社オンボーディング、RFP 技術回答
  • ブロッカー: D 社の要件が不明確(セールスに確認依頼済み)
  • 共有事項: E 社から競合乗り換えの相談あり(詳細は #sales-fde で)

技術をビジネス言語に翻訳する

エグゼクティブへの報告

「来月、四半期レビューで経営陣にプレゼンすることになりました」ソウタが言った。

「技術者が経営陣に話すときの最大の失敗は何か分かるか?」カイが聞いた。「技術的に正確すぎること……ですか?」「そうだ。正確さは美徳だが、伝わらなければ意味がない。翻訳の練習をしよう」

Before/After の翻訳例

Before(技術者の言葉):

PostgreSQL の EXPLAIN ANALYZE で確認したところ、売上集計クエリが Sequential Scan になっており、180 日分のデータで 8 秒かかっています。B-tree インデックスを追加し、日次マテリアライズドビューを導入すれば 200ms に改善できます。

After(ビジネスの言葉):

エンタープライズ顧客 4 社のダッシュボード表示が遅く、業務に支障が出ています。影響する ARR は ¥72M です。2-3 日の改修で表示速度を 40 倍に改善でき、顧客満足度の回復が見込めます。

Before(技術者の言葉):

REST API のレート制限を 100 req/s から 500 req/s に引き上げ、Redis でトークンバケットアルゴリズムを実装する必要があります。現状の Nginx ベースの制限では柔軟性が足りません。

After(ビジネスの言葉):

大口顧客 3 社の利用量が契約上限に達しつつあり、アップグレード提案の好機です。技術的な上限緩和は 1 週間で対応可能で、これにより推定 ¥15M の追加 ARR が見込めます。

「ポイントは三つだ」カイが整理した。数字で語る(影響顧客数、ARR、改善倍率)。ビジネスインパクトを先に(技術詳細は聞かれたら答える)。アクションを明確に(何をすればいいか、いつまでにできるか)。

優先度の衝突を乗り越える

セールスはX、プロダクトはY、エンジニアリングはZ

ある日、ソウタは板挟みになった。

セールス: 「大型案件のクロージングに SSO 機能が必須。来月までに欲しい」 プロダクト: 「ロードマップ上は次四半期。今はコア機能の改善が優先」 エンジニアリング: 「SSO は認証基盤の刷新が前提。最低でも 2 ヶ月はかかる」

「こういうとき、FDE はどうすべきですか?」ソウタはカイに聞いた。

裁判官になるな。通訳になれ」カイは答えた。「FDE が『セールスが正しい』とか『エンジニアリングが正しい』と判断するのは間違いだ。やるべきことは、全員が同じ事実を見て判断できるようにすることだ」

ソウタは各チームの情報を整理し、一枚の資料にまとめた。

観点事実影響
ビジネスSSO なしだと案件失注(¥30M ARR)年間売上目標の 15%
プロダクトSSO は要望ランキング 1 位(12 社)NPS 改善に直結
技術フル実装 2 ヶ月、簡易版なら 2 週間簡易版は SAML のみ
リスク簡易版は技術的負債になる可能性半年後に再実装コスト

「この資料を全員に共有して、合同ミーティングを開いた。判断したのは俺じゃない。事実を揃えたら、全員が同じ結論に辿り着いた

結果: 簡易版 SSO を 2 週間で実装し、案件をクロージング。フル実装は次四半期のロードマップに正式に組み込まれた。

WARNING

優先度の衝突で FDE が陥りがちな罠は「全員にいい顔をする」ことだ。セールスには「やります」、エンジニアリングには「無理させません」と言いたくなる。しかし、矛盾する約束は必ず破綻する。事実ベースの透明性だけが、全員が納得できる解決策を生む。

部門横断コミュニケーションハブの構築

「こういう部門横断の調整、毎回人力でやってると漏れが出ます」ソウタは言った。

「だから仕組み化するんだ。内部ツールを作れ」カイは言った。

ソウタは TechNova 社内向けの部門横断リクエスト管理システムを Rails で構築した。

データモデル

# app/models/cross_team_request.rb
class CrossTeamRequest < ApplicationRecord
  belongs_to :requester, class_name: "User"
  belongs_to :assigned_team, class_name: "Team", optional: true
  has_many :team_handoffs, dependent: :destroy
  has_many :stakeholder_updates, dependent: :destroy
 
  enum :status, {
    open: 0,
    triaged: 1,
    in_progress: 2,
    blocked: 3,
    resolved: 4
  }
 
  enum :priority, {
    low: 0,
    medium: 1,
    high: 2,
    critical: 3
  }
 
  enum :category, {
    feature_request: 0,
    bug_report: 1,
    poc_request: 2,
    rfp_response: 3,
    onboarding: 4,
    escalation: 5
  }
 
  validates :title, presence: true
  validates :business_impact, presence: true
  validates :affected_arr, numericality: { greater_than_or_equal_to: 0 }
 
  scope :actionable, -> { where(status: %i[open triaged in_progress]) }
  scope :high_impact, -> { where(priority: %i[high critical]) }
end
 
# app/models/team_handoff.rb
class TeamHandoff < ApplicationRecord
  belongs_to :cross_team_request
  belongs_to :from_team, class_name: "Team"
  belongs_to :to_team, class_name: "Team"
  belongs_to :handed_off_by, class_name: "User"
 
  validates :reason, presence: true
  validates :context_summary, presence: true
 
  after_create :notify_receiving_team
 
  private
 
  def notify_receiving_team
    CrossTeamNotifier.handoff_received(self).deliver_later
  end
end
 
# app/models/stakeholder_update.rb
class StakeholderUpdate < ApplicationRecord
  belongs_to :cross_team_request
  belongs_to :author, class_name: "User"
 
  enum :audience, {
    internal: 0,
    customer_facing: 1,
    executive: 2
  }
 
  validates :content, presence: true
 
  scope :latest_first, -> { order(created_at: :desc) }
end

リクエストルーティングサービス

# app/services/request_router.rb
class RequestRouter
  ROUTING_RULES = {
    feature_request: :product,
    bug_report: :engineering,
    poc_request: :fde,
    rfp_response: :fde,
    onboarding: :customer_success,
    escalation: :determine_by_context
  }.freeze
 
  def initialize(request)
    @request = request
  end
 
  def route!
    team_key = ROUTING_RULES[@request.category.to_sym]
 
    if team_key == :determine_by_context
      team_key = analyze_escalation_context
    end
 
    target_team = Team.find_by!(slug: team_key)
    assign_and_notify(target_team)
  end
 
  private
 
  def analyze_escalation_context
    if @request.affected_arr >= 10_000_000
      :engineering # 高ARR顧客はエンジニアリング直結
    elsif @request.business_impact.include?("churn")
      :customer_success
    else
      :fde # デフォルトはFDEがトリアージ
    end
  end
 
  def assign_and_notify(team)
    @request.update!(
      assigned_team: team,
      status: :triaged
    )
 
    CrossTeamNotifier.new_assignment(@request, team).deliver_later
 
    create_handoff_record(team) if @request.team_handoffs.any?
  end
 
  def create_handoff_record(team)
    previous_team = @request.assigned_team_was || Team.find_by!(slug: :fde)
 
    @request.team_handoffs.create!(
      from_team: previous_team,
      to_team: team,
      handed_off_by: Current.user,
      reason: "ルーティングルールによる自動振り分け",
      context_summary: build_context_summary
    )
  end
 
  def build_context_summary
    <<~SUMMARY
      リクエスト: #{@request.title}
      カテゴリ: #{@request.category}
      影響ARR: ¥#{@request.affected_arr.to_fs(:delimited)}
      顧客コンテキスト: #{@request.business_impact}
    SUMMARY
  end
end

ダッシュボードコントローラ

# app/controllers/cross_team_dashboard_controller.rb
class CrossTeamDashboardController < ApplicationController
  before_action :authorize_fde_access!
 
  def index
    @stats = {
      open_requests: CrossTeamRequest.actionable.count,
      critical_items: CrossTeamRequest.high_impact.actionable.count,
      avg_resolution_hours: average_resolution_time,
      requests_by_category: requests_by_category
    }
    @recent_requests = CrossTeamRequest
      .actionable
      .includes(:requester, :assigned_team)
      .order(priority: :desc, created_at: :asc)
      .limit(20)
  end
 
  def team_view
    @team = Team.find_by!(slug: params[:team_slug])
    @requests = CrossTeamRequest
      .where(assigned_team: @team)
      .actionable
      .includes(:stakeholder_updates)
      .order(priority: :desc)
  end
 
  def create_update
    @request = CrossTeamRequest.find(params[:request_id])
    @update = @request.stakeholder_updates.build(
      update_params.merge(author: Current.user)
    )
 
    if @update.save
      broadcast_update(@update)
      redirect_to cross_team_dashboard_path,
                  notice: "更新を投稿しました"
    else
      render :index, status: :unprocessable_entity
    end
  end
 
  private
 
  def average_resolution_time
    resolved = CrossTeamRequest.where(status: :resolved)
      .where("resolved_at > ?", 30.days.ago)
    return 0 if resolved.empty?
 
    total_hours = resolved.sum do |r|
      (r.resolved_at - r.created_at) / 1.hour
    end
    (total_hours / resolved.count).round(1)
  end
 
  def requests_by_category
    CrossTeamRequest.actionable
      .group(:category)
      .count
      .transform_keys(&:titleize)
  end
 
  def broadcast_update(update)
    ActionCable.server.broadcast(
      "cross_team_#{update.cross_team_request_id}",
      { type: "new_update", content: update.content }
    )
  end
 
  def update_params
    params.require(:stakeholder_update)
      .permit(:content, :audience)
  end
 
  def authorize_fde_access!
    unless Current.user.fde? || Current.user.admin?
      redirect_to root_path, alert: "アクセス権限がありません"
    end
  end
end

INFO

このような内部ツールを FDE 自身が構築できることが、FDE の強みだ。CS チームに「Jira のチケット見てください」と言う代わりに、各部門が本当に必要としている情報だけを見せるダッシュボードを作る。ツールの力で部門間の摩擦を減らせる。

AWS EventBridge による通知オーケストレーション

「社内ツールができたら、次は通知の自動化だ」カイが言った。「各部門のツールはバラバラだろう? セールスは Salesforce、エンジニアリングは Jira、CS は Intercom。EventBridge で統合しろ」

Loading diagram...

ソウタは AWS EventBridge を使った通知オーケストレーションを実装した。

# app/services/event_bridge_publisher.rb
class EventBridgePublisher
  def initialize
    @client = Aws::EventBridge::Client.new(
      region: ENV.fetch("AWS_REGION", "ap-northeast-1")
    )
  end
 
  def publish_cross_team_event(request, event_type)
    @client.put_events(
      entries: [
        {
          source: "technova.cross-team-hub",
          detail_type: event_type,
          detail: build_event_detail(request).to_json,
          event_bus_name: "cross-team-events"
        }
      ]
    )
  end
 
  private
 
  def build_event_detail(request)
    {
      request_id: request.id,
      title: request.title,
      category: request.category,
      priority: request.priority,
      affected_arr: request.affected_arr,
      assigned_team: request.assigned_team&.slug,
      requester: request.requester.name,
      business_impact: request.business_impact,
      timestamp: Time.current.iso8601
    }
  end
end
 
# app/services/event_bridge_rule_setup.rb
class EventBridgeRuleSetup
  def initialize
    @client = Aws::EventBridge::Client.new(
      region: ENV.fetch("AWS_REGION", "ap-northeast-1")
    )
  end
 
  def setup_routing_rules!
    create_critical_escalation_rule
  end
 
  private
 
  def create_critical_escalation_rule
    @client.put_rule(
      name: "critical-cross-team-escalation",
      event_bus_name: "cross-team-events",
      event_pattern: {
        source: ["technova.cross-team-hub"],
        "detail-type": ["request.created", "request.escalated"],
        detail: {
          priority: ["critical"],
          affected_arr: [{ numeric: [">=", 10_000_000] }]
        }
      }.to_json,
      state: "ENABLED",
      description: "高ARR顧客の重大リクエストを即時通知"
    )
 
    @client.put_targets(
      rule: "critical-cross-team-escalation",
      event_bus_name: "cross-team-events",
      targets: [
        {
          id: "slack-alert",
          arn: ENV.fetch("SLACK_ALERT_LAMBDA_ARN"),
          input_transformer: {
            input_paths_map: {
              "title" => "$.detail.title",
              "arr" => "$.detail.affected_arr",
              "team" => "$.detail.assigned_team"
            },
            input_template: '"重大リクエスト: <title> (ARR: ¥<arr>) → <team>"'
          }
        }
      ]
    )
  end
end

WARNING

EventBridge のイベントパターンで numeric フィルタを使うとき、値は数値型で送信する必要がある。JSON のシリアライズ時に文字列になっていないか注意すること。また、EventBridge のペイロード上限は 256KB なので、大量のコンテキスト情報は S3 に置いて ARN だけをイベントに含めるパターンが安全だ。

全てが交差する瞬間

3 ヶ月後。ソウタは Arclight AI 最大の顧客である MegaMart 社の案件で、全てのスキルが試される場面に直面した。MegaMart 社の CTO から連絡が入った。「推薦エンジンの精度が競合に負けている。3 ヶ月以内に改善されなければ、乗り換えを検討する」

影響する ARR は ¥120M。Arclight AI の年間売上の 8% だ。

ソウタは部門横断のコミュニケーションハブでリクエストを起票し、各部門を巻き込んだ。

セールスへ: 「MegaMart 社との関係維持が最優先です。CTO との定期ミーティングを設定してください。技術デモは私が担当します」

プロダクトへ: 「推薦エンジンの精度改善は、MegaMart 社だけでなく他 8 社からも要望があります。影響 ARR 合計 ¥280M。ロードマップの優先度見直しを提案します」

エンジニアリングへ: 「現行アルゴリズムのベンチマーク結果を添付します。collaborative filtering から hybrid approach への移行で推定 15% の精度向上が見込めます。技術検証に 2 週間、実装に 4 週間の見積もりです」

CS へ: 「MegaMart 社の利用データを分析した結果、推薦機能の設定が最適化されていません。設定チューニングだけで 5% の改善が可能です。今週中にオンサイトサポートを入れてください」

カイがレビューした。「完璧だ。各部門に対して、その部門が理解できる言葉で、その部門が行動できるアクションを伝えている」

2 ヶ月後、推薦エンジンの精度は 23% 向上した。MegaMart 社の CTO は契約を 3 年に延長した。

ソウタは振り返った。技術力だけでは、この結果は出せなかった。セールスの顧客関係、プロダクトの方向転換、エンジニアリングの技術力、CS のオンサイト対応——全ての部門が同じゴールに向かって動いたからこそ、達成できた。

「FDE って何屋ですか、と聞かれたら」ソウタはカイに言った。

「今ならどう答える?」

全部門の交差点に立って、技術で橋を架ける人です」

カイは微笑んだ。「それが部門横断コラボレーションの答えだ」

INFO

FDE の部門横断コラボレーションは、単なるコミュニケーションスキルではない。技術的な深さビジネスの文脈理解人間関係の構築が三位一体となったとき、初めて機能する。一朝一夕には身につかないが、意識して実践を積めば、必ず到達できるスキルだ。