Mert Özen Her Satırda Daha İleri
Proje Yönetimi Estimation ve Planning — #03

Point Dışında Tahmin Yöntemleri: Three-point/PERT, Wideband Delphi, Work Breakdown ve Reference Class Forecasting

Mert Özen 14 Kas 2026 14 dk 1 görüntülenme
Point Dışında Tahmin Yöntemleri: Three-point/PERT, Wideband Delphi, Work Breakdown ve Reference Class Forecasting

Belirsiz bir işi tahmin etmek için işi parçalara böl, her parçaya üç nokta ver, toplamı tek tek toplamak yerine simüle et ve sonucu benzer geçmiş işlerin sapmasıyla dışarıdan kontrol et. Bookstore'un toplu sipariş dilimi için C# simülasyonu, en olası sürelerin toplamının (21 gün) yalnızca %3,1 ihtimalle tuttuğunu, %85 değerin 28 gün, geçmiş epic'lere göre düzeltilmiş değerin 34 gün olduğunu gösteriyor.

Sürüm notu: Bu yazıdaki hesaplayıcı ve simülasyon .NET 10 ile derlenip çalıştırılmıştır; reference class forecasting kısmı Bent Flyvbjerg'in 2006 tarihli From Nobel Prize to Project Management makalesini esas alır.

Önce Tahmin Et

Planning poker'dan bir story 8 point ile ve büyük bir soru işaretiyle çıktı: ISBN'si (kitabın uluslararası standart numarası) olmayan satırları kitap adıyla eşleştirmek. Selin, sitedeki serilerin ortak örnek uygulaması Bookstore'un (bir online kitapçı) Kurumsal Sipariş'inde en çok bu dilimden emin olmak istiyor, çünkü Nevin Hanım'ın okulundan gelen listelerin bir kısmında ISBN hiç yok. Ekip "listeyle toplu sipariş" dilimini dokuz iş paketine bölüyor ve her biri için gün cinsinden bir "en olası" süre yazıyor. Toplam 21 developer-day (bir geliştiricinin bir iş günü).

Selin'in sorusu basit: "21 gün mü diyelim?" Her paketin en olası süresini toplamak, toplamın en olası süresini verir mi? Bu dokuz paketin hepsinin en olası sürede bitme ihtimali sence ne kadar: %50 mi, %20 mi, daha mı az? Bir de şunu düşün: her paket için bir de "her şey ters giderse" süresi yazılsa ve bunlar toplansa, o toplam gerçekçi bir en kötü durum olur mu?

Work Breakdown: Tahmin Edilebilir Parçalar

Büyük ve belirsiz bir işi tahmin etmenin ilk adımı onu tahmin edilebilir parçalara bölmek. Work breakdown structure, kısaca WBS (işin tamamını hiyerarşik olarak daha küçük teslimatlara bölen yapı), proje yönetiminde bunun adı. Yazılım ekibi için pratik kurallar şunlar: her parça birkaç günden büyük olmamalı, parçalar toplandığında işin tamamını kapsamalı ve ilk yazıda gördüğümüz unutulan işler, yani test, review ve deploy, kendi satırında durmalı.

Story bölmek ile work breakdown aynı şey değil. Requirements Analysis serisinin "Story Mapping ve Story Bölme" yazısında story'leri kullanıcıya değer veren dikey dilimlere böldük. Work breakdown ise bir story'nin ya da dilimin içindeki işi tahmin için ayırır; parçaların tek başına kullanıcıya değer vermesi gerekmez. "Tests, review, deployment" bir story değildir ama tahminde bir satırdır.

Bookstore ekibinin dökümü şöyle: yapıştırılan listeyi ayrıştırmak, ISBN doğrulama ve katalogda arama, ISBN'siz satırları kitap adıyla eşleştirmek, doğrulama sonucu ekranı, kurumsal indirim kuralı, büyük adetler için stok kontrolü, taslak kaydetme, satın alma emri numarasıyla siparişi göndermek ve son olarak test, review ve deploy.

Three-Point Tahmin ve PERT

Tek sayı yerine her parça için üç sayı yazılır: iyimser (O), en olası (M) ve kötümser (P). O, her şey yolunda giderse; M, en sık beklenen durumda; P, makul ölçüde kötü giden bir durumda geçen süredir. P "dünyanın sonu" değildir; kabaca yirmi denemeden birinde aşılacak bir değer olarak düşünülebilir.

PERT (Program Evaluation and Review Technique; üç tahminin ağırlıklı ortalamasına dayanan planlama yöntemi), 1958'de ABD Donanması'nın Special Projects Office birimi ile Booz Allen Hamilton'ın Polaris füze programı için geliştirildi. Bir parçanın beklenen süresi (O + 4M + P) / 6, standart sapması (P − O) / 6 olarak hesaplanır. Formül, sürenin beta dağılımına uyduğu varsayımından gelen bir yaklaşımdır: en olası değer dört kat ağırlık alır ve O ile P arası kabaca altı standart sapma kabul edilir.

Parçaları toplarken ortalamalar doğrudan toplanır. Belirsizlik ise toplanmaz: parçalar birbirinden bağımsızsa varyanslar toplanır ve toplamın standart sapması varyans toplamının kareköküdür. Bu, kötü giden bir paketin başka bir paketin iyi gitmesiyle kısmen dengelendiği varsayımıdır. İşin önemli kısmı da tam burada; birazdan bu varsayımın neden tehlikeli olduğunu konuşacağız.

Kod: PERT, Simülasyon ve Dışarıdan Bakış

Hesaplayıcıyı serinin console uygulamasına ekliyoruz. İş paketlerinin yanında, reference class için Bookstore'un son sekiz epic'inin ilk tahminini ve gerçekleşen süresini de aynı dosyaya koyuyoruz:

namespace Bookstore.Planning;

public sealed record WorkPackage(string Name, double Optimistic, double MostLikely, double Pessimistic)
{
    public double PertMean => (Optimistic + 4 * MostLikely + Pessimistic) / 6;
    public double PertSigma => (Pessimistic - Optimistic) / 6;

    // Triangular dağılımdan örnek (inverse CDF)
    public double Sample(Random rng)
    {
        double u = rng.NextDouble();
        (double a, double m, double b) = (Optimistic, MostLikely, Pessimistic);
        double split = (m - a) / (b - a);
        return u < split
            ? a + Math.Sqrt(u * (b - a) * (m - a))
            : b - Math.Sqrt((1 - u) * (b - a) * (b - m));
    }
}

public sealed record PastEpic(string Name, double EstimatedWeeks, double ActualWeeks)
{
    public double Ratio => ActualWeeks / EstimatedWeeks;
}

public static class Pert
{
    // "Listeyle toplu sipariş" dilimi, developer-day cinsinden O / M / P
    static readonly WorkPackage[] Packages =
    [
        new("Parse pasted list (ISBN-10/13)", 1, 2, 4),
        new("ISBN validation + catalog lookup", 1, 2, 3),
        new("Match rows without ISBN by title", 2, 4, 10),
        new("Validation result screen", 2, 3, 6),
        new("Institutional discount rule", 0.5, 1, 2),
        new("Stock check for large quantities", 1, 2, 5),
        new("Save as draft", 1, 2, 4),
        new("Submit with PO number + e-mail", 1, 2, 4),
        new("Tests, review, deployment", 2, 3, 6),
    ];

    // Dışarıdan bakış: Bookstore'un son 8 epic'i, ilk tahmin ve gerçekleşen
    static readonly PastEpic[] Epics =
    [
        new("CheckoutV2", 6, 9),
        new("Returns", 4, 6.2),
        new("CampaignEngine", 5, 8),
        new("MobileWeb", 8, 11),
        new("SearchRelevance", 3, 3.6),
        new("GiftCards", 3, 4.8),
        new("AdminReports", 2, 2.8),
        new("StockReservation", 2.5, 3),
    ];

    public static void Run()
    {
        Console.WriteLine($"{"Work package",-34} {"O",4} {"M",4} {"P",4} {"PERT",6}");
        foreach (WorkPackage w in Packages)
            Console.WriteLine($"{w.Name,-34} {w.Optimistic,4} {w.MostLikely,4} {w.Pessimistic,4} {w.PertMean,6:F2}");

        double sumMostLikely = Packages.Sum(w => w.MostLikely);
        double mean = Packages.Sum(w => w.PertMean);
        double sigma = Math.Sqrt(Packages.Sum(w => w.PertSigma * w.PertSigma)); // bağımsızlık varsayımı
        Console.WriteLine();
        Console.WriteLine($"Sum of most likely   : {sumMostLikely:F1} days");
        Console.WriteLine($"Sum of pessimistic   : {Packages.Sum(w => w.Pessimistic):F1} days");
        Console.WriteLine($"PERT total           : {mean:F1} days, sigma {sigma:F1}");
        Console.WriteLine($"PERT 85% (normal)    : {mean + 1.04 * sigma:F1} days");

        const int Runs = 10_000;
        Random rng = new(2026);
        List<double> totals = [.. Enumerable.Range(0, Runs).Select(_ => Packages.Sum(w => w.Sample(rng))).Order()];
        Console.WriteLine();
        Console.WriteLine($"Simulation, {Runs:N0} runs (triangular):");
        foreach (int p in new[] { 10, 50, 85, 95 })
            Console.WriteLine($"  p{p,-2} {Stats.Percentile(totals, p),5:F1} days");
        Console.WriteLine($"  P(total <= sum of most likely) = {totals.Count(t => t <= sumMostLikely) * 100.0 / Runs:F1}%");

        // Reference class: geçmiş epic'lerde gerçekleşen / tahmin oranı
        List<double> ratios = [.. Epics.Select(e => e.Ratio).Order()];
        Console.WriteLine();
        Console.WriteLine($"Reference class: {Epics.Length} past epics, actual / estimate");
        Console.WriteLine($"  ratios: {string.Join(" ", ratios.Select(r => r.ToString("F2")))}");
        Console.WriteLine($"  p50 uplift x{Stats.Percentile(ratios, 50):F2}, p80 uplift x{Stats.Percentile(ratios, 80):F2}");
        Console.WriteLine($"  sum of most likely x p80 uplift = {sumMostLikely * Stats.Percentile(ratios, 80):F1} days");
    }
}

Simülasyon her çalıştırmada her paket için triangular dağılımdan bir süre çekiyor ve dokuzunu topluyor. Triangular dağılım O, M ve P'yi doğrudan kullanır ve hesaplaması kolaydır; Sample metodu tek bir rastgele sayıyı inverse CDF (kümülatif dağılım fonksiyonunun tersi) ile süreye çeviriyor. Random'a sabit seed (2026) veriyoruz ki çıktı tekrarlanabilir olsun. Program.cs'e case "pert": Pert.Run(); break; satırını ekleyip çalıştırıyoruz:

dotnet run --project src/Bookstore.Planning -- pert
Work package                          O    M    P   PERT
Parse pasted list (ISBN-10/13)        1    2    4   2.17
ISBN validation + catalog lookup      1    2    3   2.00
Match rows without ISBN by title      2    4   10   4.67
Validation result screen              2    3    6   3.33
Institutional discount rule         0.5    1    2   1.08
Stock check for large quantities      1    2    5   2.33
Save as draft                         1    2    4   2.17
Submit with PO number + e-mail        1    2    4   2.17
Tests, review, deployment             2    3    6   3.33

Sum of most likely   : 21.0 days
Sum of pessimistic   : 44.0 days
PERT total           : 23.2 days, sigma 2.0
PERT 85% (normal)    : 25.3 days

Simulation, 10,000 runs (triangular):
  p10  22.3 days
  p50  25.4 days
  p85  28.2 days
  p95  29.9 days
  P(total <= sum of most likely) = 3.1%

Reference class: 8 past epics, actual / estimate
  ratios: 1.20 1.20 1.38 1.40 1.50 1.55 1.60 1.60
  p50 uplift x1.40, p80 uplift x1.60
  sum of most likely x p80 uplift = 33.6 days

Çıktıyı Okumak

En olası sürelerin toplamı 21 gün. Simülasyon, 10.000 olası gelecekten yalnızca %3,1'inde toplamın 21 günün altında kaldığını söylüyor. Selin'e "21 gün" demek, otuz projeden birinde tutacak bir söz vermek olurdu. Nedeni dağılımların şekli: süre sıfırın altına inemez ama yukarı doğru uzun bir kuyruğu vardır. Kitap adıyla eşleştirme paketinde M = 4, P = 10; işler ters gidince altı gün kaybedilir, iyi gidince en fazla iki gün kazanılır. Dokuz paketin her birinde bu asimetri aynı yöne itiyor.

Öteki uç da yanıltıcı: kötümser sürelerin toplamı 44 gün, ama simülasyonun %95'lik değeri 29,9 gün. Dokuz paketin hepsinin aynı anda kötü gitmesi çok düşük bir olasılık; P'leri toplamak gerçekçi bir en kötü durum değil, ped eklemenin matematiksel bir yolu.

PERT formülü 23,2 gün ortalama ve 2,0 gün standart sapma veriyor; normal dağılım varsayımıyla %85'lik değer 25,3 gün. Simülasyonun ortancası ise 25,4, %85'liği 28,2 gün. Aradaki fark dağılım seçiminden: PERT'in beta yaklaşımı M'ye dört kat ağırlık veriyor, triangular dağılımın ortalaması ise (O + M + P) / 3 ve sağa çarpık işlerde daha kötümser. Hangisinin doğru olduğunu ekibin kendi geçmişi söyler; ama farkın büyüklüğü bile önemli bir bilgi: yalnızca hangi formülü seçtiğin, %85'lik tarihi üç gün kaydırıyor.

Daha büyük bir sorun bağımsızlık varsayımı. Kitap adıyla eşleştirme beklenenden zor çıkarsa, doğrulama ekranı da daha karmaşık olur, testler de uzar. Paketler birlikte kötüleşir. Bu durumda gerçek dağılım simülasyonun gösterdiğinden daha geniştir. Kod bunu modellemiyor; modellemenin en basit yolu, birbirine bağlı paketleri tek bir pakette birleştirip ona geniş bir O–P aralığı vermek.

Bu simülasyon, Kanban serisinin "Service Level Expectation ve Monte Carlo" yazısındakinden farklı bir şey yapıyor. Oradaki Monte Carlo ekibin geçmiş throughput'undan örneklem alır ve ekip bir şey tahmin etmez; yöntem ancak geçmişe benzeyen işler için geçerlidir. Buradaki simülasyon ise ekibin bu iş için verdiği üç noktalı tahminlerden örneklem alıyor. Geçmiş verisi olmayan, yeni türden bir iş için kullanılabilir, ama girdi yine insanların tahminleri olduğu için ilk yazıdaki iyimserlikten kurtulamaz. Bunu düzeltmenin yolu dışarıdan bakmak.

Reference Class Forecasting: Dışarıdan Bakış

Kahneman ve Tversky, planning fallacy'nin karşısına "outside view" adını verdikleri yaklaşımı koydular: işi kendi içinden, adım adım hayal ederek değil, benzer işlerin gerçekte nasıl sonuçlandığına bakarak tahmin etmek. Bent Flyvbjerg bu fikri büyük altyapı projelerine uyguladı ve reference class forecasting adıyla yöntemleştirdi. 2006 tarihli makalesinde tahminlerin yanılmasını iki nedene bağlar: optimism bias, yani iyimserlik yanlılığı, ve strategic misrepresentation, yani projeyi onaylatmak için maliyetin ve sürenin bilerek düşük gösterilmesi. Amerikan Planlama Derneği 2005'te planlamacılara bu yöntemi geleneksel yöntemlerin yanında kullanmalarını tavsiye etti.

Yöntem üç adımdan oluşur. Önce yeterince benzer geçmiş işlerden bir referans sınıfı seçilir. Sonra bu sınıftaki işlerin gerçekleşen sonuçlarının dağılımı çıkarılır; yazılımda en kullanışlısı, gerçekleşen sürenin ilk tahmine oranı. Son olarak yeni iş bu dağılıma yerleştirilir: istenen güven seviyesine karşılık gelen oran, ilk tahmine bir çarpan olarak uygulanır.

Çıktının son bloğu bunu yapıyor. Bookstore'un son sekiz epic'inde gerçekleşen / tahmin oranı 1,20 ile 1,60 arasında; ortancası 1,40, %80'liği 1,60. Hiçbir epic tahmininden önce bitmemiş. En olası toplam olan 21 güne %80'lik çarpanı uygulayınca 33,6 gün çıkıyor. İçeriden bakışın %85'lik değeri 28,2 gündü. Aradaki beş günlük fark, ekibin geçmişte de olduğu gibi bu dökümde bir şeyleri atlamış olabileceğini söylüyor. Gerçekçi bir cevap iki bakışı birlikte vermek: "Görevleri tek tek tahmin ettiğimizde %85 ihtimalle 28 gün; geçmiş epic'lerimizin sapmasını hesaba katınca 34 güne kadar çıkabilir."

İlk yazıda Emre'nin cevabını ekibin feature geçmişiyle düzelttiğimiz hesap da aslında bunun küçük bir örneğiydi. Asıl zorluk referans sınıfını seçmek. Çok geniş seçersen, mesela şirketin bütün projeleri, Bookstore ekibine benzemeyen işler girer. Çok dar seçersen elinde iki örnek kalır. Kurumsal Sipariş için en iyi sınıf, dış bir sisteme ya da başka bir ekibe bağımlı olan, birkaç sprint süren epic'ler olurdu; sekiz epic'lik liste bunun için yeterince ayrıntılı değil ama hiç yoktan çok daha iyi.

Wideband Delphi ve Uzman Tahmini

Tahmin ekipteki tek bir kişiden geliyorsa, o kişinin varsayımları da tek kaynaktır. Delphi yöntemi 1950'lerde RAND (ABD'li araştırma kuruluşu RAND Corporation) bünyesinde uzman görüşlerini birleştirmek için geliştirildi: uzmanlar anonim tahmin verir, bir koordinatör sonuçları toplayıp herkese bir özet gösterir ve uzmanlar bu özeti görerek yeniden tahmin eder. Barry Boehm ile John Farquhar 1970'lerde buna turlar arasında grup tartışması ekleyip Wideband Delphi adını verdiler; yöntem Boehm'ün 1981 tarihli Software Engineering Economics kitabıyla yaygınlaştı.

Uygulamada süreç şöyle işler. Koordinatör işi ve kapsamı anlatır. Her katılımcı, kendi iş paketi listesini de çıkararak anonim tahmin verir. Koordinatör tahminleri isimsiz olarak gösterir ve tartışma en uçtaki tahminlerin arkasındaki varsayımlara odaklanır: biri bir paketi hiç düşünmemiş mi, biri başka bir teknoloji mi varsaymış? Sonra yeni bir anonim tur yapılır. Birkaç turdan sonra tahminler genelde birbirine yaklaşır; yaklaşmıyorsa işin kendisinde netleşmemiş bir şey vardır.

Planning poker'ın Wideband Delphi'den ilham aldığı açık: anonim tahmin, farkların konuşulması, yeni tur. Fark ölçek ve hızda. Poker story düzeyinde ve dakikalar içinde çalışır; Wideband Delphi ise büyük ve tek seferlik işler, mesela bir entegrasyon projesinin tamamı için, iş paketi listeleriyle birlikte, saatler süren bir oturumdur. Bookstore gibi küçük bir ekipte iki geliştiricinin tahmini çok az bağımsız görüş demek; benzer bir entegrasyonu daha önce yapmış başka bir ekipten birini oturuma davet etmek tahmine en çok katkı yapan adım olabilir.

Spike ve Time-box: Tahmin Edilemeyeni Önce Öğrenmek

Bazı işler tahmin edilemez çünkü ekip ne yapacağını henüz bilmiyor. Kitap adıyla eşleştirme böyle bir iş: başlık yazımları farklı ("Suç ve Ceza" ile "Suc ve Ceza"), aynı adla birden çok baskı var, bazen yazar adı da yanlış yazılmış. O = 2, P = 10 aralığı bu bilgisizliğin sayısal karşılığı. Bu aralığı tartışarak daraltamazsın; ancak öğrenerek daraltabilirsin.

Spike, belirsizliği azaltmak için yapılan, süresi önceden sabitlenmiş bir araştırma işidir. Bookstore ekibi iki günlük bir spike planlıyor: Requirements Analysis serisinde toplanan okul listelerindeki ISBN'siz satırları, başlık ve yazar adını normalize edip katalogla eşleştiren basit bir prototiple deneyecekler ve otomatik eşleşme oranını ölçecekler. Spike'ın çıktısı kod değil, bir cevap: oran %90'ın üstündeyse basit normalizasyon yeter ve paket 3–4 güne iner; %60 civarındaysa satın alma sorumlusuna aday kitap listesi gösteren bir ekran gerekir ve paket büyür.

Spike'ın time-box'ı önemli. İki gün dolduğunda ekip ne öğrendiyse onunla karar verir; spike, sessizce uzayan bir geliştirme işine dönüşmemeli. Bu, SDLC (Software Development Life Cycle; yazılımın fikirden emekliliğe yaşam döngüsü) serisinin "Incremental, Iterative ve Spiral Modeller: Riski Erken Görmek" yazısında anlattığımız spiral modelin küçük ölçekli karşılığı: en belirsiz konuyu önce, ucuz bir denemeyle ele almak.

Üretimde Dikkat

Three-point tahminleri ve gerçekleşen süreleri kaydet. Bir sonraki tahminde, ekibin P'lerinin ne sıklıkla aşıldığını görebilirsin. P'ler yirmide bir değil de beşte bir aşılıyorsa, ekip kötümser tahminlerinde de iyimser demektir ve aralıkları genişletmek gerekir. Reference class tablosunu da aynı şekilde her epic bittiğinde güncelle; çarpanın kendisi zamanla değişir.

Developer-day ile takvim gününü karıştırma. 28 developer-day, iki geliştiriciyle 14 iş günü demek değildir: toplantılar, destek talepleri, review'lar ve başka işler araya girer. Developer-day'i takvime çevirmek için ya ekibin geçmişte bu tür bir işe gerçekte ayırabildiği oranı kullan ya da bu dönüşümü dördüncü yazıdaki gibi ekip düzeyinde velocity üzerinden yap. Sprint kapasitesi hesabı Scrum serisinin "Sprint ve Sprint Planning: Why, What, How ve Sprint Goal" yazısında.

Simülasyonun sonucunu tek sayıya indirmeden paylaş. Selin'e giden not, %50, %85 ve dışarıdan bakışın değerini birlikte vermeli; hangi paketlerin aralığı en çok genişlettiğini de söylemeli, çünkü o paketler spike için ilk adaylardır.

Sık Düşülen Tuzaklar

  • En olası süreleri toplamak: her paketin M'si gerçekçi olsa bile toplamları neredeyse hiç tutmaz.
  • Kötümser süreleri toplamak: gerçekçi bir en kötü durum değil, kimsenin kullanamayacağı kadar geniş bir ped.
  • Tek kişinin tahminini ekip tahmini gibi sunmak: tek bir kişinin varsayımları, tek bir kişinin kör noktalarıyla gelir.
  • Geçmiş veriyi tutmadan "tecrübeye" güvenmek: tecrübe, kayıt olmadan, hatırlanan başarılardan oluşur; gecikmeler unutulur.
  • Bağımsızlık varsayımını sorgulamamak: birlikte kötüleşen paketler, aralığı formülün gösterdiğinden genişletir.
  • Spike'ı time-box'sız yapmak: öğrenme işi, fark edilmeden asıl geliştirmeye dönüşür ve tahminin kendisi kaybolur.

Küçük Bir Deneme

★☆☆ Üzerinde çalıştığın bir sonraki işi beş-on pakete böl, her biri için O, M ve P yaz ve Packages dizisini kendi verinle değiştir. M toplamının altında kalma olasılığın kaç çıktı?

★★☆ WorkPackage'a, triangular yerine PERT'in beta dağılımından örnek çeken ikinci bir Sample metodu ekle. İpucu: beta(α, β) için α = 1 + 4(M − O)/(P − O) ve β = 1 + 4(P − M)/(P − O); beta örneğini iki gamma örneğinin oranıyla üretebilirsin. İki dağılımın %85 değerleri ne kadar farklı?

★★★ Bağımlılığı modelle: "Match rows without ISBN by title" paketi kendi aralığının üst yarısına düştüğünde doğrulama ekranı ve test paketlerinin süresini %30 artıran bir kural ekle. %85 değeri ne kadar kaydı? Bağımsızlık varsayımının bedelini bu farkla göster.

Artık tek bir dilim için içeriden ve dışarıdan bakan bir tahminimiz var. Bir sonraki yazıda bütün Release 1'e geri dönüyoruz: geçmiş velocity'den aralık çıkarmak, burn-up grafiğinde kapsamı ayrı göstermek ve 1 Mart sabit kalacaksa kapsamın nasıl esneyeceğine karar vermek.