27 Kasım 2020 Cuma

Link Local Adress - İşletim Sistemi Tarafından Atanan Private Adres

Giriş
Link Local Address kavramı hem IPv4 hem de IPv6'da var. Link Local adres işletim sistemi tarafından kendi kendine atanır. Amacı atanmış bir IP adresi alıncaya kadar bir adrese sahip olmaktır. Buna aynı zamanda APIPA (Automatic Private IP Addressing) de deniliyor

IPv6 Link Local Address
Açıklaması şöyle. Aslında tanım olarak fe80::/10 aralığında ancak 54 bit 0 olduğu için pratikte fe80::/64 aralığındadır.
2.5.6.  Link-Local IPv6 Unicast Addresses

   Link-Local addresses are for use on a single link.  Link-Local
   addresses have the following format:

   |   10     |
   |  bits    |         54 bits         |          64 bits           |
   +----------+-------------------------+----------------------------+
   |1111111010|           0             |       interface ID         |
   +----------+-------------------------+----------------------------+
54 bit şu anda 0 ile dolduruluyor. İleride link-local adreslerin özelleştirilmesi için kullanılabilir.
İlk ve son 64 bit için açıklama şöyle
First 64 bits are always fe80:: according to RFC 4291. Last 64 bits are interface ID. Interface ID is modified MAC address.
Link Local Adresi görmek için şöyle yaparız.
# ifconfig test.123
test.123: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1450
    inet6 fe80::a03a:a8ff:fe0c:d976  prefixlen 64  scopeid 0x20<link>
    ether a2:3a:a8:0c:d9:76  txqueuelen 1000  (Ethernet)
    RX packets 0  bytes 0 (0.0 B)
    RX errors 0  dropped 0  overruns 0  frame 0
    TX packets 0  bytes 0 (0.0 B)
    TX errors 0  dropped 5 overruns 0  carrier 0  collisions 0
IPv4
Bu adres IPv4 için 169.254.0.0/16. Yani 169.254.0.0 – 169.254.255.255 aralığında. Bu adres sadece tavsiye. Açıklaması şöyle
The purpose of these self-assigned link-local addresses is to facilitate communication with other hosts within the subnet even in the absence of external address configuration (via manual input or DHCP). Unlike in IPv6, implementation of IPv4 link-local addresses is recommended only in the absence of a normal, routable address. Hosts pseudo-randomly generate the last two octets of the address to mitigate address conflicts. Because of the broadcast nature of some local networking protocols (for example, Microsoft's NetBIOS), hosts may be able to detect one another even without any preexisting knowledge of the address scheme.
Client Side Conflict Resolution
İşletim sistemi kendisine bir adres seçtikten sonra, bu adresin hâlihazırda kullanılmadığını kontrol etmek zorundadır. Zaten bu yüzden Link Local Address tek bir adres değil de bir subnetten oluşuyor. Kontrol için ARP mesajları kullanılır. Açıklaması şöyle
It is almost impossible for two devices to choose the same IP for themselves.

Each device picks up an address and then tests to see if it is already in use, by broadcasting ARP Probe packets. All conflict cases are well covered, such as repeating requests when no answers arrive, ARP request for the same IP by another device, and others.

Finally the device announces itself to the network via ARP with its chosen IP, guaranteed by the algorithm to be unique (or almost guaranteed).

For details, see RFC 5227 that contains the standard that all ad-hoc networks must follow.

Link-local address is a variant of this algorithm, first implemented in MacOS 9 in 1998, where each device picks a random address as its first try. The standard here is RFC 3927.


Fisher-Yates Shuffling Algoritması

Giriş
Bu algoritma Array gibi random access veren veri yapılarında kolayca kullanılır. 

Bir diziyi shuffle yapmak için, tüm dizi üzerinde tersten dolaşıp, bir sayı üretir ve üzerinde olduğumuz elemanla yer değiştiririz. Üretilen sayı üzerinde bulunduğumuz [0,indeks] aralığındadır. Tersten dolaşmamızın sebebi RNG'lerin genelde [0,x) aralığında sayı üretmesi.

Düzden de dolaşsak, tersten de dolaşsak, önemli olan daha önce shuffle ettiğimiz kısma dokunmamak. Bu yüzden nextInt(i+1) şeklinde kodluyoruz.

Bir çok programlama dilinde shuffle işlemi için çözüm bulunuyor. Böylece Fisher-Yates gibi algoritmalar bilmek zorunda kalmıyoruz.

Örnek - Java
Şöyle yaparız. nextInt() exclusive olduğu için uzunluğun bir fazlası verilir.
// Implementing Fisher–Yates shuffle
static void shuffleArray(int[] ar)
{
  Random rnd = new Random();
  for (int i = ar.length - 1; i > 0; i--)
  {
    int index = rnd.nextInt(i + 1);
    // Simple swap
    int a = ar[index];
    ar[index] = ar[i];
    ar[i] = a;
  }
}
Örnek - C++
Aynı algoritmanın C++ ile gerçekleştirilmiş için şöyle yaparız
rand() exclusive olduğu için uzunluğun bir fazlası verilir.
void shuffleString (std::string& s){
  for(int i=s.size()-1;i>0;i--){
    std::swap(s[rand()%i+1],s[i]));
  }
}
Örnek - C
Şöyle yaparız. Burada kendi rand_ranged_inclusive() metodumuz olduğu için uzunluğun bir küçüğü verilir.
// Fisher-Yates (Durstenfield)
int shuffle(int arr[], int len)
{
  for (int i = 0; i < len; ++i) {
    int j = rand_ranged_inclusive(i, len-1);
    SWAP_INT(arr[i], arr[j]);
  }
}

Game Engine Tasarımı

Giriş
Game engine tasarımı ile ilgili basit notlarım şöyle

Entity Eklemek
Elimizde bir oyun senaryosu editörü olsun. Bu editör ile bir oyun senaryosu hazırlamak isteyelim. Senaryo editörü ile temel CRUD işlemlerini yapabilmek gerekir.

Burada tasarımda hem Senaryo Editörü hem de Game Engine birer entity container olarak düşünülebilir.

Game Engine çalıştırılması (yani tick'lenmesi gereken) her türlü nesne için bir container'dır
Senaryo Editörü ise kullanıcının ekranda gördüğü nesneler için bir container'dır. Bu fark önemli çünkü 

1. Kullanıcı sadece  kendisini ilgilendiren nesneleri ekranda görür
2. Game engine içinde çalışan ancak ekranda görünmesi gerekmeyen nesneler de olabilir.

Dolayısıyla varsayım şöyle
Senaryo Editörü, Game Enginde'ki container'a view olur. Muhtemelen her iki yapı farklı thread'ler üzerinde çalışacakları için aralarında bir listener mekanizması gerekir. Yani Game Engine'deki değişiklikler, Senayo Editörüne de bir şekilde yansıtılacaktır

1. Nesne Eklemek
Yeni nesne bir şekilde Senaryo Editöründe seçilir. 
- Seçim GUI'deki menü, tree gibi bir bileşen aracılığıyla olabilir. 
- Mevcut bir  nesnenin kopyala/yapıştır yöntemiyle eklenmesi olabilir

Aslında nasıl olduğu çok önemli değil. Y

- Yeni nesne Game Engine'e eklenir. 
- Game Engine haliyle Senaryo Editörü'nü bilgilendirir ve Senaryo Editörü de kendi container'ına bu nesneyi ekler.
- Nesne ilgili ekran açılır ve güncellemeler yapılır. Tamam düğmesi ile Game Engine'e güncel nesne gönderilir.
-Game Engine haliyle Senaryo Editörü'nü bilgilendirir
- Böylece her iki container'da asenkron olmalarına rağman senkronize olurlar


Clean Code - Functions (Uncle Bob)

Giriş
Şeklen şöyle. Buradaki başlıklar
- General Rules
- Design Rules
- Understandability tips
- Name Rules
- Function Rules
- Comment Rules
- Source code structure
- Object and data structures
- Tests
- Code smells
Function için kitaptaki başlıklar şöyle
Small
Do One Thing
One Level of Abstraction per Function
Reading Code from Top to Bottom: The Stepdown Rule
Switch Statements
Use Descriptive Names
Function Arguments : Flag Arguments, Dyadic Functions, Triads, Argument Objects
Have No Side Effects : Output Arguments
Command Query Separation
Prefer Exceptions to Returning Error Codes
Don’t Repeat Yourself
Structured Programming
Top to Bottom
Açıklaması şöyle
In a crux, your logic should be written as a hierarchy of methods, like an article, let’s say you are reading an article by the headline you can understand what is all about? after reading the first paragraph you will have a crisp understanding of the topic and if you want details then go the other paragraphs.

The same way your methods must be structured. The top-level method must say about the feature, if you go to the next level it will tell about the algorithm, lastly, it will give the implementation details. I like to compose complex logic as a Template method pattern. 
Örnek
Şöyle yaparız
public void displayEvenNumberFromInputSource(){         
  List<Integer> numberList =  processInputSource();
  List<Integer> evenList = findEven(numberList);
  display(evenList);
}
Switch Statements
Örnek
Elimizde şöyle bir kod olsun
public Money calculatePay(Employee e) throws InvalidEmployeeType {
  switch (e.type) {
    case COMMISSIONED:
      return calculateCommissionedPay(e);
    case HOURLY:
      return calculateHourlyPay(e);
    case SALARIED:
      return calculateSalariedPay(e);
    default: 
      throw new InvalidEmployeeType(e.type);
  }
}
Açıklaması şöyle. Burada birden fazla "iş" yapma cümlesine itiraz ediliyor.
There are several problems with this function. First, it's large, and when new employee types are added, it will grow. Second, it very clear does more than more thing. Third, it violates the Single Responsibility Principle(SRP) because there is more than one reason for it to change. Fourth, it violates the Open Closed Principle(OCP) because it must change whenever new types are added. But possibly the worst problem with this function is that there are an unlimited number of other functions that will have the same structure.
Bu kod şu hale getirilebilir. Ancak yine birden fazla "iş" yapma devam ediyor. Aslında "birden" fazla iş yapma durumu anlayışa bağlı.
public Employee createEmployee(int employeeType) throws InvalidEmployeeType {
  switch (employeeType) {
    case COMMISSIONED:
      return new CommissionEmployee(e);
    case HOURLY:
      return new HourlyEmployee(e);
    case SALARIED:
      return new SalariedEmployee(e);
    default: 
      throw new InvalidEmployeeType(e.type);
  }
}

public Money calculatePay(int employeeType) throws InvalidEmployeeType {
  Employee e = createEmployee(e.type);
  return e.calculatePay();
}
Function Arguments
Number of Arguments yazısına taşıdım

26 Kasım 2020 Perşembe

Preemptive Scheduling Nedir? - Geçişli Zamanlama

Giriş
Preemptive scheduling Türkçe' ye "Geçişli Zamanlama" olarak tercüme edilmiş. Ben de bazen aynı tabiri kullanacağım.

Preemptive Scheduling multitasking yapan işletim sistemleri konusunda karşımıza çıkan bir konu. Günümüzdeki çoğu işletim sistemi bu yöntemi kullanıyor
Linux and most modern OSes use preemptive multitasking ...
Multitasking Ne Demek
Multitasking, tek bir işlemcinin çok hızlı şekilde process'ler arası geçiş yapıp hepsini sırayla çalıştırması demek. Böylece sanki tüm process'ler çalışıyormuş izlenimi doğuyor.

Multiprocessing Ne Demek
Elimizde birden fazla işlemcinin olması durumunda olur. 

- Symmetric Multiprocessing kullanılıyorsa işletim sistemine adanmış bir işlemci vardır. Diğer uygulamalar geri kalan işlemciler tarafından çalıştırılır

- Asymmetric Multiprocessing kullanılıyorsa işletim sistemine adanmış bir işlemci yoktur. Tüm işlemciler her şeyi çalıştırırlar.

Preemptive Scheduling Nedir?
Kısaca, önceliği yüksek olan bir process hazır (ready) hale gelirse, çalışmakta olan önceliği düşük uygulama zorla durdurulur ve önceliği yüksek olan uygulamaya zaman tanınır. (preemption). 

Yani zorla güzellik yapılır :) 

Preemption İçin Uygulamanın İşbirliği Gerekmez
Her şey tamamen işletim sistemin kontrolünde olduğu için uygulamanın işbirliği gerekmez. Açıklaması şöyle
Linux is a preemptive multitasking operating system, i.e. it multitasks without requiring running programs’ cooperation.
Preemption İçin Kernel'ın Çalışması Gerekir
Preemption İçin Kernel en az bir işlemci tarafından çalıştırılıyor olmalıdır. Açıklaması şöyle
Note that the kernel can be configured to not control a subset of a system’s CPUs itself; but at least one CPU in the system must always be under the kernel’s control.
Çünkü kernel belli aralıklarla preemptive scheduling yapar. Açıklaması şöyle
Basically in preemptive multitasking the kernel will be fired periodically from a timer, and whenever the kernel is in control (when the timer interrupt happens or a system call is invoked) the kernel will save the current process' context and then switch to the next process' context. That's called a context switch where the whole state of the process including all thread information, register values... are saved and restored to make the process continue to run from the exact point it was preempted without even knowing it hasn't run continuously.
Cooperative Multitasking İle Farkı Nedir?
Açıklaması şöyle
... the kernel has complete control over the time each process is allowed to run and will preempt a process if it runs for too long, unlike cooperative multitasking where a process will pass control whenever it likes
Execution Times vs Response Times
Şeklen şöyle. Şekilde Medium Priority iş 2 defa Preemption'a yani Geçişli Zamanlamaya uğruyor. Yani 2 Execution Time yaptıktan sonra iş bitiyor. Dolayısıyla Response Time daha farklı bir süre.
Açıklaması şöyle
The response time for a job is the time between when it becomes active and the time it completes.






24 Kasım 2020 Salı

ffmpeg Komutu Main Options File Format Seçeneği

Giriş
Açıklaması şöyle
Force input or output file format. The format is normally auto detected for input files and guessed from the file extension for output files, so this option is not needed in most cases.
Girdi veya çıktının formatını belirtir. Bu seçeneğe çoğunlukla ihtiyaç duyulmaz çünkü format otomatik olarak bulunabilir.

-f alsa
Bir örnek burada

-f concat 
Eğer dosyaları birleştirmek istersek şöyle yaparız.
ffmpeg -f concat -i joinlist.txt -c copy joinedfile.mp4
joinlist.txt dosyasının içi şöyle olmalıdır.
file 'vid1.avi'
file 'black_screen_video.mp4'
file 'vid2.avi'
file 'black_screen_video.mp4'
file 'vid3.avi'
file 'black_screen_video.mp4'
...
Sanırım dosya şöyle de olabilir.
Part1.mp4
Part2.mp4
Part3.mp4
-f dshow - directshow cihazından görüntü alır
USB cameranın yakaladığı görüntü http üzerinden sunulur.
ffmpeg -f dshow -i video="A4TECH USB2.0 PC Camera" -r 25 -s 320x240 
 -vcodec libx265 -preset ultrafast -tune zerolatency  
 http://119.81.216.43:8090/feed1.ffm
-f rtp
RTP Nedir?
RTP bir transport protokolü. Açıklaması şöyle
RTP is real time protocol used for transporting audio and video in real time. Transport used can be unicast, multicast or broadcast, depending upon transport address and port. Besides transporting RTP does lots of things for you like packetization, reordering, jitter control, QoS, support for Lip sync.....
Örnek
İki tane rtp akımın birleştirmek ve yine rtp olarak sunmak için şöyle yaparız.
ffmpeg -f rtp -i rtp://196.1.110.249:8977 -f rtp -i rtp://196.1.110.249:8999
-filter_complex amix=inputs=2:duration=longest:dropout_transition=3
-f rtp rtp://192.168.105.207:8004
-f rtsp
RTSP Nedir?
 Açıklaması şöyle
... you can think of RTSP as a "VCR controller", the protocol allows you to specify which streams (presentations) you want to play, it will then send you a description of the media, and then you can use RTSP to play, stop, pause, and record the remote stream. The media itself goes over RTP.
RTMP Nedir?
RTMP de bir streaming protokolü. Sadece TCP kullanır. FlashPlayer içindir.

Örnek
rtps'i flv olarak vermek için şöyle yaparız
ffmpeg -i rtsp://url -c copy -an -f flv rtmp://localhost/live
-f v4l2 formatı
-i ile belirtilen device'tan v4l2 olarak okuyup diğer device'lara yazmak için şöyle yaparız.
# duplicate webcam to virtual devices
ffmpeg -f v4l2 -i /dev/video0 -f v4l2 /dev/video1 -f v4l2 /dev/video2

23 Kasım 2020 Pazartesi

Top K Elements veya Top K Frequent Elements - System Design Sorusu Olabilir

Giriş
Top K Element algoritmaları bana en uzun, en kısa en X ölçütüne uyan ilk K elemanı ver anlamına gelir. Algoritma kabaca şöyle. Yani önce elemanları bir ölçüte göre sayıyoruz ve daha sonra bunları bir minheap veri yapısına ekleyip, Top K elemanı buluyoruz.
1. We first go through the array and count each element’s frequency, creating a distribution map.
2. We then go through the key-value pairs in the distribution map, and maintain a min-heap of size k based on frequency.
Dağıtık (Distributed) Top K
Burada MapReduce kullanmak gerekiyor. Bu algoritmanın biraz daha gelişmiş hali "Top K Frequent Elements in a Time Window"

Top K Değerini Bilmiyorsak
Eğer en uzun elemanların tamamını ver deseydi, Top K eleman algoritmaları kullanılamaz çünkü, ölçütü sağlayan kaç tane nesne olduğunu bilmiyoruz.

Top K Elements Çözümleri
Bu algoritmaların bir çok çözümü var

Örnek - Listeyi Dolaşarak PriorityQueue İle Sıralamak
En uzun 2 string'i bulmak isteyelim. Şöyle yaparız. 
int topK = 2;
PriorityQueue<String> pq = new PriorityQueue<>(String::length);

for (String word : wordList){
  pq.offer(word);
  if (pq.size() > topK){
    pq.poll(); //En kısayı sil
  }
}

while (!pq.isEmpty()){
 pq.remove(); 
}
Örnek - Tree'yi Dolaşarak PriorityQueue İle Sıralamak
Şöyle yaparız
public List<Integer> closestKValues(TreeNode root, double target, int k) {
  // 1. Create a Priority Queue, sorted on diffs
  PriorityQueue<double[]> pq = new PriorityQueue<double[]>(
Comparator.comparing(a -> a[1]));
  Stack<TreeNode> stack = new Stack<>();
        
  stack.push(root);
        
  // 2. Traverse tree iteratively and place items in PQ
  while (!stack.isEmpty()) {
    TreeNode current = stack.pop();
            
    pq.add(new double[] {current.val /* value */,
Math.abs(current.val - target /* diff */ )});
            
    if (current.left != null) {
      stack.push(current.left);
    }
            
    if (current.right != null) {
      stack.push(current.right);
    }        
  }
        
  // 3. Prepare the output with needed K-values.
  List<Integer> result = new ArrayList<>();
        
  for (int i = 0; i < k; ++i) {
    if (!pq.isEmpty()) {
      result.add((int) pq.poll()[0]);
    }
  }
  return result;
}
Örnek - Girdi Olan Dizi Sıralı Olmalı
En yüksek frekans (frequency) değerine sahip K elementi bulmak isteyelim. Dizi sıralı olduğu için şöyle yaparız. process() metodu her değerden kaç tane olduğunu bulur ve addOrReplace() metodunu çağırır. countMap içinde Top K Elements sırasız biçimde bulunur
class TopN {
  private final int maxSize;
  private Map<Long, Long> countMap;

  public TopN(int maxSize) {
    this.maxSize = maxSize;
    this.countMap = new HashMap(maxSize);
  }

  private void addOrReplace(long value, long count) {
    if (countMap.size() < maxSize) {
      countMap.put(value, count);
    } else { //Top K sınırına geldik
      Optional<Entry<Long, Long>> opt = countMap.entrySet().stream()
.min(Entry.comparingByValue()); //En küçük frekansa sahip elemanı bul Entry<Long, Long> minEntry = opt.get(); if (minEntry.getValue() < count) { //Eğer mevcut frekans daha küçükse, sil countMap.remove(minEntry.getKey()); countMap.put(value, count); } } } public void process(long[] data) { long value = data[0]; long count = 0; for (long current : data) { if (current == value) { ++count; } else { addOrReplace(value, count); value = current; count = 1; } } addOrReplace(value, count); } }

20 Kasım 2020 Cuma

Lessons Learned - Öğrenilen Dersler. Günümüzdeki Adı Retrospektif Toplantısı

Giriş
Lessons Learned sadece yazılım süreçlerinde kullanılan bir şey değil. Bir çok başka süreçlerde de kullanılan bir yöntem. Ancak bu yazıda konuya yazılım projeleri bakışıyla bakacağız.

İsmi Lessons Learned olmasa da Çevik (Agile) Süreçler de Lessons Learned benzeri şeylerin yapılmasını istiyorlar. Scrum - Retrospektif Toplantısı aslında özünde bir Lessons Learned toplantısı

1. Ne Zaman Yapılır?
"Lessons Learned", her release veya proje sonunda yapılır. Amaç geriye yönelik muhasebe olarak değerlendirilebilir.

2. Bu Toplantı Sistematik Yapılmalıdır
Lessons Learned sistematik bir şekilde yapılırsa faydalı olabilir. Yani bir gündem olmalı, bulgular kayıt altına alınmalıdır. Bu toplantının geçmişe odaklanmasını sağlamak ve toplantının tamamen "Next Release Planning Meeting" e dönüşmemesini sağlamak önemli.

3. Gündem
Gündem aşağıdaki maddeleri kapsayabilir.
- Sistemin Durumu : Müşterinin istekleri ve yorumları
- Test Durumu : Biten, devam eden test faaliyetleri
- Yazılımın Durumu : Biten, devam eden yazılım faaliyetleri
- Çeşitli Metrikler : Organizational Process Performance alanında toplanan veriler.
- Hata Durumu : Açılan, kapanan, doğrulanmayı bekleyen hatalar
- Kilometre taşları : Önümüzdeki önemli kilometre taşları
- Riskler : Mevcut riskler ve ele alma yöntemleri
- Eğitim ve Destek Durumu : Müşteriye verilen eğitim ve destek faaliyetleri

4. Toplantıda Parmak Gösterme (Suçlama) Olmamalıdır
Buna Blameless Culture deniliyor. İnsanlar birbirlerini üstü kapalı da olsa suçlarlarsa, herkes hatasını da saklamaya çalışır.

5. Yeni Katılanların Gözlemleri
Ekibe yeni katılanların gözlemleri çok faydalı olabilir. Farklı firmalardaki işleyiş taze kan getirebilir.

6. Bazı Örnekler
Özellikle NASA'dan çok güzel Öğrenilen Dersler çıkıyor. NASA uçuşlarında insanlar hatalar yapıyorlar. Kimi hatalar çok tehlikeli bile olabiliyor. Bazı kazalar sadece kablolama hatası bile olabiliyor.

Root Cause Analysis
Eski sistemlerde Root Cause Analysis yapması daha kolaydı. Ancak artık sistemler çok karmaşık hale geldi. Bazen öyle oluyor ki birden fazla sistemin aynı anda belli miktarda hata vermesi sonucunda ölümcül bir hata oluşuyor. Bu durumda suç kimde ? Bu tür sistemler için şöyle deniliyor
There Is No Root Cause!
Stop again. Think about the complex system. We already saw that keeping a system running is a balancing act. Finding a root cause would imply that there is a single thing that caused the error. If good behavior is explained by complexity, how can a single cause explain faulty behavior?

It can’t. It‘s a logical fallacy. There is no root cause.

Scrum - Burndown Chart

Burndown Chart Ne İşe Yarar ?
Açıklaması şöyle. Yani kestirim ile gerçeğin farkını gösteriyor.
A burndown chart is useful not to forecast the completion date, but to show if your actual progress is happening as the progress you forecasted. You have an ideal line on the burndown, and an actual line that shows you if you are behind (above) or ahead (below) of what you ideally forecasted. Seeing that allows you to take action and correct your trajectory if you still want to deliver on the desired date.
İdeal Burndown Chart Eğrisi Nasıl Olmalı Deniliyor ?
Normalde beklentinin sprint sonunda aynı eğimdeki bir çizgiyi takip ederek y ekseninde 0 noktasına gelmek olduğu söyleniyor. 

Ancak günlük ilerlemenin böyle olmadığını düşünenler de var. Sebebi ise ekibin önce büyük işlere odaklanması ve bu işlerin vakit alması. Vakit harcanırken sanki Burndown Chart üzerinde ilerleme olmuyormuş gibi görünüyor. Açıklaması şöyle.
The ideal line is a fantasy. A typical Sprint contains stories of various sizes, some small, medium, and large tasks. The team will jump on the large tasks firsthand if anyone has the capacity, some of the medium tasks. The small tasks are saved for last.
Dolayısıyla bu tür çizelgeleri yanlış bulanlar şöyle diyor. Aslında ben de bu fikre katılıyorum :)
The ideal burndown chart is an indication that you're doing something of low added value.

It is not you, the theory is wrong.
Burndown Chart Çeşitleri
İki tip burndown chart kullanılabilir.  İlki sprint içindeki ilerlemeyi ikincisi ise proje içindeki ilerlemeyi gösterir.

Sprint İçindeki İlerleme Chart'ı
Sprint içindeki ilerlemeyi Excel ile tutmak ta mümkün. Aşağıdaki gibi bir tablo bile iş görür. Biten işler işaretlenir.
Resource | Code Review | Unit Test | Integration Test | Completion
Story 1
Story 2