<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Dimitry Dushkin</title>
    <description>The latest articles on DEV Community by Dimitry Dushkin (@sky2high0).</description>
    <link>https://dev.to/sky2high0</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F197240%2F5f297162-e7c7-46ab-a680-78fad6beeea3.jpg</url>
      <title>DEV Community: Dimitry Dushkin</title>
      <link>https://dev.to/sky2high0</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sky2high0"/>
    <language>en</language>
    <item>
      <title>Task planning algorithm in TypeScript: real-life problem solved with graph theory</title>
      <dc:creator>Dimitry Dushkin</dc:creator>
      <pubDate>Tue, 31 Mar 2020 10:28:09 +0000</pubDate>
      <link>https://dev.to/sky2high0/task-planning-algorithm-in-typescript-real-life-problem-solved-with-graph-theory-32n2</link>
      <guid>https://dev.to/sky2high0/task-planning-algorithm-in-typescript-real-life-problem-solved-with-graph-theory-32n2</guid>
      <description>&lt;p&gt;In this article, I’ll present the algorithm which helps to answer the main question of all project planning efforts:&lt;/p&gt;

&lt;blockquote&gt;
&lt;h1&gt;
  
  
  When will it be done?
&lt;/h1&gt;
&lt;/blockquote&gt;

&lt;p&gt;A more formal representation of this problem sounds like: “Having some tasks which might depend on each other and some folks which can do those tasks when a milestone can be reached?”&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--D6FSbU5s--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/4404/1%2AKBgI05DqeNusAfdSVWQRUA.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--D6FSbU5s--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/4404/1%2AKBgI05DqeNusAfdSVWQRUA.jpeg" alt="Weekly sprint planning meeting in essence"&gt;&lt;/a&gt;&lt;em&gt;Weekly sprint planning meeting in essence&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A Little Backstory
&lt;/h2&gt;

&lt;p&gt;Since summer 2019 I work as a tech lead. Currently, I’m responsible for 3 different projects with a team of 7 developers, 2 managers, 2 designers, and several departments to cooperate with.&lt;/p&gt;

&lt;p&gt;For task tracking, we’re using our internal tool &lt;a href="https://yandex.ru/tracker/"&gt;Yandex Tracker&lt;/a&gt; which is mostly like Jira. But it has no tools that’ll help to find the answer for an eternal question: “When?”. That’s why from time to time we manually sync tasks with &lt;strong&gt;Omniplan&lt;/strong&gt;. Turned out that it’s the tool that solves almost all project planning problems and moreover it has an &lt;strong&gt;auto-planning feature&lt;/strong&gt; so all situations when one assignee has workload over 100% are resolved automatically.&lt;/p&gt;

&lt;p&gt;Still, it has some drawbacks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Slow and unreliable project sync between team mates based on a local copy syncing&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;MacOS only&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Quite hard to sync it with our issue tracker&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Pricey: $200 and $400 for Pro edition&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So I’ve decided to try to make my own Omniplan version with blackjack and hookers that would be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Web-based&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Simple syncing with our tracker&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;With real-time collaboration&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The most exciting part was to make &lt;strong&gt;a scheduling engine&lt;/strong&gt;. I didn’t understand why only Omniplan has such an essential feature. Now I do.&lt;/p&gt;

&lt;p&gt;So this article is about scheduling.&lt;/p&gt;

&lt;h2&gt;
  
  
  Developing a scheduling engine
&lt;/h2&gt;

&lt;p&gt;First I’ve done some research. I’ve googled for solving scheduling tasks in general and found a lot of about &lt;a href="https://en.wikipedia.org/wiki/Gantt_chart"&gt;Gantt&lt;/a&gt;, &lt;a href="https://en.wikipedia.org/wiki/Program_evaluation_and_review_technique"&gt;PERT&lt;/a&gt;, but haven’t found any practical algorithms.&lt;/p&gt;

&lt;p&gt;Then I looked for open-source libraries and found only one: &lt;a href="https://github.com/bryntum/chronograph"&gt;Bryntum Chronograph&lt;/a&gt;. It seems like something I was looking for all the time. They even have &lt;a href="https://github.com/bryntum/scheduler-performance"&gt;benchmarks&lt;/a&gt;. But, well, talking honestly I didn’t understand &lt;a href="https://github.com/bryntum/chronograph/blob/master/src/chrono/Graph.ts"&gt;any of its code&lt;/a&gt; and almost complete lack of documentation didn’t help either. I thought maybe, what if I could write it from scratch with less code.&lt;/p&gt;

&lt;p&gt;So, as usual, I’ve tried to draw the problem.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OQ_F3KvH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2452/1%2AkFofS0xLfilw8y7OQ1mCfg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OQ_F3KvH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2452/1%2AkFofS0xLfilw8y7OQ1mCfg.png" alt="Timeline of tasks before scheduling"&gt;&lt;/a&gt;&lt;em&gt;Timeline of tasks before scheduling&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;While drawing it I’ve probably got the most important insight: tasks can be represented as a directed graph and edges are not only the explicit dependencies but also &lt;em&gt;implicit dependencies by the same assignee&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The following algorithm should lead to such tasks arrangement:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KaVlJz-M--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2948/1%2AP6QZut58YL7YrighHWxChw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KaVlJz-M--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2948/1%2AP6QZut58YL7YrighHWxChw.png" alt="Timeline of tasks after scheduling"&gt;&lt;/a&gt;&lt;em&gt;Timeline of tasks after scheduling&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Let’s consider what a task is:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YrPaxj7q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2796/1%2AG9nxvpUvc09hFu4LpI0QQA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YrPaxj7q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2796/1%2AG9nxvpUvc09hFu4LpI0QQA.png" alt="Task anatomy"&gt;&lt;/a&gt;&lt;em&gt;Task anatomy&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;There are some not so obvious properties of a task:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Duration&lt;/strong&gt;. The task is being done only during &lt;em&gt;business&lt;/em&gt; days and the number of &lt;em&gt;business&lt;/em&gt; days is an &lt;em&gt;estimation&lt;/em&gt; of a task. So in this example the task starts on 2 March, has an estimation of 6 days, so it will end on 9 March (&lt;em&gt;not 7 March&lt;/em&gt;), because 7 and 8 March are holidays.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Position&lt;/strong&gt;. In this model, we assume that tasks with lower positions (same as a higher priority) should be done earlier than a task with higher positions (or lower priority).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Progress&lt;/strong&gt;. It is a portion of a task that can be represented in percents but in fact, it is a number of days that were spent on a task. For example, if a task is estimated up to 4 days and, then progress is 75%, that 1 day left to task completion.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So TypeScript type is as follows (&lt;em&gt;ID is just an alias for string&lt;/em&gt;):&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h2&gt;
  
  
  The algorithm
&lt;/h2&gt;

&lt;p&gt;In essence, the algorithm should change start and end dates of the tasks in the following way:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Tasks should start today if it is possible&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It should be impossible to start a task today if it has other tasks as prerequisites that are unfinished. In that case, a task should start right after the last prerequisite’s end date.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Pretty simple, huh? 🙈&lt;/p&gt;

&lt;p&gt;The main steps of the algorithm are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;**Build a graph from tasks. **Make edges taking into account explicit dependencies and implicit dependencies by the same assignee.&lt;/li&gt;
&lt;/ol&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;2. Make a *reverse graph&lt;/strong&gt;* to be able to detect sinks (node without outgoing edges), sources (node without ingoing edges) and disconnected nodes (nodes without edges at all). *A reverse graph *is the same as an original directed graph but with inverted edges.&lt;/p&gt;

&lt;p&gt;To visit all nodes we need &lt;a href="https://en.wikipedia.org/wiki/Depth-first_search"&gt;depth-first search traversal&lt;/a&gt;, so I‘ve made a universal DFS helper function as well:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;3. Visit every node (task) and update a start and end dates.&lt;/strong&gt; Visiting should be performed starting from the task with higher priority&lt;/p&gt;

&lt;p&gt;If a task is a source (it is not a prerequisite for any other task and task’s assignee has no other task to do before this task) or a task is disconnected node (it has no dependencies and it is not prerequisite for any other tasks) then we start task today.&lt;/p&gt;

&lt;p&gt;Otherwise, a task has prerequisites and its start date should be set to the latest end date of its prerequisites tasks.&lt;/p&gt;

&lt;p&gt;Also, it is important to correctly &lt;strong&gt;update an end date while setting a start date of a task&lt;/strong&gt;. We should take into account actual business days and the progress of a task.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h2&gt;
  
  
  What can be improved
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Circular dependency detection.&lt;/strong&gt; If cycles are present throw an error because that means that some task A has as prerequisite task B and task B has as prerequisite task A, which is a classic problem of &lt;a href="https://en.wikipedia.org/wiki/Circular_dependency"&gt;circular dependency&lt;/a&gt; and the problem should be resolved manually. Tasks A and B do not necessarily have an explicit dependency on each other, there are might some other tasks between them. It is a well-known algorithm I think I’ll add it soon. For my current use case, it is not that important.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Probably the most important feature which leads to a more complex solution is introducing &lt;strong&gt;a desirable start date&lt;/strong&gt; of a task. I didn’t research it yet, but if someday it will grow to a standalone web-service I think it should be done. Now, this feature can be mitigated by setting proper priorities of tasks.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;For the enterprise-grade solution, it is important to take into account vacations and public holidays. I think &lt;strong&gt;updateStartDate&lt;/strong&gt; function can be quite easily updated to support this functionality.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;For my case having a &lt;strong&gt;day&lt;/strong&gt; as a time quant was okay, but I believe for some people &lt;strong&gt;hour&lt;/strong&gt;-based planning might be important. I think implementing an hour-based quant can also introduce some complications to code.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The code from the article you can find &lt;a href="https://github.com/DimitryDushkin/project-tasks-scheduling-engine"&gt;here&lt;/a&gt;. You can grab and use it as an &lt;a href="https://www.npmjs.com/package/project-tasks-scheduling-engine"&gt;NPM package&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I’m wondering if a presented solution has some flaws. If you spotted some please contact me here or on &lt;a href="https://github.com/DimitryDushkin/project-tasks-scheduling-engine/issues"&gt;issues section in Github&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>algorithms</category>
      <category>taskplanning</category>
    </item>
  </channel>
</rss>
