Dan and the Actionable Team have created a brilliant tool called Actionable which has many more capabilities than I will show here. This is a straw-man version, BUT I learned a lot of the mechanics behind the scenes in creating this and I hope you will too.
I love Google Sheets, so I’m going to work there, but you can probably do this in Excel with minimal changes. You can follow along in this sheet that you can make a copy of to tweak.
The book does an amazing job of describing much of the background to why and how to do data-driven prognosis than I can even summarize here, so I’m just going to cut to the chase. The two main questions I wanted to be able to answer are:
Turns out that these questions can be answered with some statistical analysis based on some very basic data. We only need two data points per work item; when did we start the work (“Could you please?”) and when was the work completed (“Thank you”). If you are using JIRA these items can easily be found as Created and Resolved dates.
Since we are going to produce a prognosis we want the answer to contain a span and a confidence percentage. “It will take less than 25 days with 80% confidence”, for example.
Let’s do the simple things first
I’ve always found it useful to keep the raw data around and then base all subsequent calculations on that data, rather than copy data between tabs. So let’s first create some raw data.
Open a Google Sheet and create a tab called Raw data
. Then make some data up (you can use mine if you want). If you are using a tool like JIRA you can export a CSV file and import it here.
Here’s some example data:
ID | Status | Created Date | Resolved Date |
---|---|---|---|
ABC-1234 | Closed | 2018-07-02 | 2018-07-02 |
ABC-1235 | Closed | 2018-07-02 | 2018-07-02 |
ABC-1236 | Closed | 2018-06-29 | 2018-07-02 |
I’ve included ID
and Status
here, but that is not needed to do the calculations we are going to do
Flow time is what Dan calls it but that could also be called Lead time or Cycle time, but that gets into theoretical Lean arguments. Flow Time is good enough for us.
Flow time is the time between start and stop. +1, since if you started and stopped work on the same day that would count as one day’s worth of work. This is easy to calculate in Google Sheets; =DATEDIF(Created Date, Resolved Date, "d") +1
Let’s create a new tab for “Flow time” and populate it with the following columns:
First I import all relevant rows using the powerful QUERY
-function
=QUERY(RawData!A:D, "SELECT A, B, C, D WHERE B = 'Closed'")
This will give you all the columns from the RawData-tab and then we can create an additional column to calculate Flow time in the E-column:
=DATEDIF(C2, D2, "d") +1
Fill the entire column with this formula, calculating the Flow time in days for each row.
Now we want to calculate the Median (50%), 80%-percentile and 95%-percentiles for this data. This will be used as our prognosis data. Add these forumlas in the F, G and H columns
F | G | H |
---|---|---|
Median | 80-percentile | 95-percentile |
=PERCENTILE(E:E, 0.5) |
=PERCENTILE(E:E, 0.8) |
=PERCENTILE(E:E, 0.95) |
Now fill all of those columns too, and calculate the percentiles for each row. You could also copy the value from F2, G2, and H2 if you want to improve the performance.
Select all the columns and create a scatter plot using the Flow time, Median, 80, and 95 percentiles. For the percentiles format the series to have Point size
set to None and only show a trend line. It should look like this
And here we can now see that 50% of all values fall beneath the red, median line, which is at 8 days. And similar for the 80% and 95% line.
We can now say this:
We know this because we have data from 266 items.
That’s a pretty good outcome from just tracking start and stop dates.
That was pretty simple to do, but pretty useful. The natural question to follow up with is then “How long will it take to complete all of it?”, or “We have 100 user stories in the upcoming release - when is that done?”.
It’s easy to fall into the trap of just multiplying the flow time with the number of items and say “Since the flow time was 8 days and the backlog has 100 items it will take 100 days, with 50% certainty”. But that will give the wrong answer, instead, we need to look at the throughput of the system - how many things are completed each day.
Luckily that can also obtained from the same two dates as before. We simply count the number of items that were completed every day.
Open a new tab and call it “Throughput”. In the A-column we’ll get all the dates, in descending order, from the RawData-tab: =SORT(UNIQUE(RawData!D2:D))
In the B-column, we can now count the number of times each date occurs in the raw data, using the following formula: =COUNTIF(RawData!$D$2:$D, A2)
and repeat the formula for each row in the B-column.
UPDATED: If you download some data from JIRA you will get timestamps for your Resolved dates and we really need only the date part. Here’s a QUERY that reads the Resolve date (in the X
-column of the sheet Jira raw data
) and counts the number of occurances. I.e. the formulas above all at once:
=QUERY(INDEX(DATEVALUE('Jira raw data'!X:X)),"SELECT Col1,COUNT(Col1) group by Col1 ORDER BY Col1 DESC Label Col1 'Date', COUNT(Col1) 'Throughput'")
This outcome, that particular number of items completed for each day, is one way that it played out. But we want to look into the future and thinking that it will play out the same way doesn’t make sense. I think about it like this: that it would be the same is just one way it can happen - the future holds many potential variants like that.
We want to predict a possible outcome of many uncertainties. This is what Monte Carlo Simulations are for “The Monte Carlo simulation is a mathematical technique that predicts possible outcomes of an uncertain event”
Using our example reality, we will simulate many different possible future outcomes, or timelines and see which ones are more likely. That sounds hard, but the algorithm is pretty simple. Here’s my description of it.
A:B
-columnsAs you might no function out-of-the-box accomplishes the Monte Carlo Simulation, for this data. There are ways to do this by copying data into cells etc. But that will be a one-off, where we want to have this repeatable.
Time to crack out the code editor! Let’s create the function MonteCarloSimulateBacklogCompletion
. I want to pass 4 parameters:
You can define custom functions in Google Sheets by going to Extensions->App Script and creating a function in the code editor. The code we write here is some variant of JavaScript and I had to write a few helper-functions to perform the Monte Carlo Algorithm, but here is the outline of the code. I’ll show the entire code at the end of the post
function MonteCarloSimulateBacklogCompletion(througputDataRangeName, numberOfSimulationRuns, backlogSize, startDate) {
const throughputValues = rangeToFlatArray(SpreadsheetApp.getActive().getRange(througputDataRangeName));
var projectedDates = []
for(var i=0; i<=numberOfSimulationRuns; i++){
var days = daysToReachZero(throughputValues, backlogSize);
projectedDates.push(addDays(startDate, days))
}
const uniqueDays = getUniqueDays(projectedDates).sort()
const histogramData = uniqueDays.map(ds => {
return [ds, countMatchingDates(ds, projectedDates)]
})
return histogramData;
}
function countMatchingDates(dateStringToCheck, simulatedDays) {
return simulatedDays.filter(d => dateStringToCheck === d.toISOString().split('T')[0]).length
}
function daysToReachZero(values, backlogSize) {
var daysCounter = 1;
var leftOfBacklog = backlogSize;
while(leftOfBacklog > 0) {
leftOfBacklog -= randomFromArray(values);
daysCounter += 1;
}
return daysCounter;
}
Some comments on the code:
rangeToFlatArray
numberOfSimulationRuns
number of times
daysToReachZero
below)startDate
to get the projected end date.projectedDates
daysToReachZero
function
randomFromArray
helper function I’ve writtenAt this point, I now have a long list of projected end dates in projectedDates
. One for each simulation. We now need to count how many times each date occurs in that list.
The returned data will, in other words, be an array of arrays with two values: the projected date and the number of times that date occurs in projectedDates
.
[
["2024-05-01", 1],
["2024-05-02", 4],
// etc
]
getUniqueDays
helper-function
.sort()
will sort these dates in descending orderuniqueDays.map
countMatchingDates
that compares the date part of the string.map
is returned to the user, and will be output in two columns.Again - the detailed code can be found at the end of the blog post.
MonteCarloSimulateBacklogCompletion
functionThat was very cool! I love me some JavaScript. But let’s put the MonteCarloSimulateBacklogCompletion
to good use in our Google Sheet.
I’m going to put the simulation parameters in some cells in the sheet to be able to rerun it as needed:
Row | D | E |
---|---|---|
1 | Monte Carlo Parameters | |
2 | Backlog size | 100 |
3 | Start date | 2023-03-14 |
4 | Number of iterations | 100000 |
Now I can use my function, remembering that the output will be as many rows as in E4
I added my function in G2 like this: =MonteCarloSimulateBacklogCompletion("B2:B", $E$4, $E$2, $E$3)
That will take some time to run (5 seconds ca for 100000 rows) but then we would have two columns with the simulation results.
You can now create a simple Column chart for all the values in G:H
and it would look something like this:
If you update the parameters the simulation will rerun and the graph will be updated. However, during the run, the graph disappears and other values cannot be calculated.
That is cool but the prognosis is a bit hard to see. We wanted to get some certainties and ranges…
For this, we need to calculate the percentiles for this data, which is a little bit tricky. For the 50-percentile we need to find the middle date, for example. We will do that by using INDEX
that finds a value from a range based on a row number.
That makes our job pretty easy - we just count the number of values and multiply it with 0.5
(for 50%, for 80% we use 0.8
etc.). Here are my calculations:
Row | D | E |
---|---|---|
6 | Number of values | =(COUNT(H:H)) |
7 | 50 percentile | =INDEX(G:G, $E$6 * 0.5) |
8 | 80 percentile | =INDEX(G:G, $E$6 * 0.8) |
9 | 95 percentile | =INDEX(G:G, $E$6 * 0.95) |
Now we can answer the initial question like this:
If we started today, 2024-03-14, we would finish 100 items from the backlog:
- With 50% certainty 2024-05-16
- With 80% certainty 2024-06-01
- With 95% certainty 2024-06-10
We used a Monte Carlo Simulation with 100000 iterations and based it on our throughput for the last 178 days.
I’m very proud that I managed to get this together and working so nicely. The value of this is that you with very little input-collection work (you probably already have this available from JIRA) and can easily produce a prognosis for completion to people around you. This report is based on actual data rather than guesses and estimations.
Here is the full code listing to implement the MonteCarloSimulateBacklogCompletion
as described above. Note the comments that introduce some nice help for the user in Google Sheets.
function rangeToFlatArray(range) {
var values = range.getValues();
var flatArray = values.flat(); // Flatten the array of arrays
return flatArray.filter(value => value !== '');
}
function randomFromArray(arr) {
return arr[Math.floor(Math.random() * arr.length)];
}
function addDays(date, days) {
var result = new Date(date);
result.setDate(result.getDate() + days);
return result;
}
function getUniqueDays(dateArray) {
return dateArray.filter((date, i, self) =>
self.findIndex(d => d.getTime() === date.getTime()) === i
).map(d => d.toISOString().split('T')[0])
}
function countMatchingDates(dateStringToCheck, simulatedDays) {
return simulatedDays.filter(d => dateStringToCheck === d.toISOString().split('T')[0]).length
}
function daysToReachZero(values, backlogSize) {
var daysCounter = 1;
var leftOfBacklog = backlogSize;
while(leftOfBacklog > 0) {
leftOfBacklog -= randomFromArray(values);
daysCounter += 1;
}
return daysCounter;
}
/**
* Simulates end dates to complete the backlog using the Monte Carlo Simulation
*
* @param {range} througputDataRangeName A range of actual things completed per day
* @param {number} numberOfSimulationRuns The number of runs to do. More is better, but also slower. 1000 is probably good enough.
* @param {number} backlogSize The size of the backlog, and number of items, to use in the simulation
* @param {date} startDate The date to use as the start date for the calculation
* @return unique dates and times each date occurred in the simulation [date, number of occurrances]
* @customfunction
*/
function MonteCarloSimulateBacklogCompletion(througputDataRangeName, numberOfSimulationRuns, backlogSize, startDate) {
const throughputValues = rangeToFlatArray(SpreadsheetApp.getActive().getRange(througputDataRangeName));
var projectedDates = []
for(var i=0; i<=numberOfSimulationRuns; i++){
var days = daysToReachZero(throughputValues, backlogSize);
projectedDates.push(addDays(startDate, days))
}
const uniqueDays = getUniqueDays(projectedDates).sort()
const histogramData = uniqueDays.map(ds => {
return [ds, countMatchingDates(ds, projectedDates)]
})
return histogramData;
}
I still remember when I understood the difference between effectiveness and efficiency. Those are the same word in Swedish, but when I understood the difference a lot of things made sense when it comes to agile and lean stuff. Read more here
Last week I had another revelation like that - when I realized the difference between value and valueable. According to Dave Farley it’s a common mistake he sees for teams to adopt user stories effectively. I think he is right.
What David Farley is after is how we tend to make user stories too big since we are confusing value and valuable. Value to a user doesn’t mean value to a user. He then continues to talk about a treasure chest. A chest full of gold coins is certainly valuable, but each coin has value. The reasons we want to make the stories small are plentiful; it’s easier to manage, implement, fix, deploy, and test… just about anything we do as a software development team. But it’s also faster to achieve the true goal, with smaller batches than with big batches, as any lean simulation/game will show you. For example this:
In short; smaller user stories are more effective to do. It is, however, important that each story has some value to a user, but it doesn’t have to be valuable. But we want each story to have some value to the user.
This is because the whole idea of user stories is that they are told from the perspective of the user and the value the user will get from the feature. We don’t want to describe HOW but rather just the WHAT. This is easier to do if you focus on value for a user.
For example;
At this point, I saw an analogy to how test-driven development should be practiced. I love when I see these connections in different areas because it makes it easier to know when I’m on the right path. An approach that has helped me in one area is easy to apply to another when I know the guiding principles. When using TDD to write code you follow a few steps to ensure that you are writing something small, useful, and relevant tests.
The first few iterations of your code are rarely the version that you end up with, but it has some value. Just like user stories.
Also like user stories, we learn by doing the work. The more tests and production code iterations we do the better we understand the problem and can make even better solutions in the end.
The distinction between value and valuable can be seen in bullet 3 above. The “return a constant”-step is rarely what is needed, but it still has value in the process of writing tests. We, the developers, got some value from it. It’s doubtful that a function called addIntegers
should always return the constant 2
, but it was value for us at the time.
Making user stories small and valuable is hard. Very hard. But making them small with value for a user is luckily both much easier and a good way to write user stories.
]]>Hold on? What are we testing here? Now I’m only testing the framework code
I’ve had this conversation many times, mostly with myself, and a few days ago with Johan. It might feel and seem like a failure but it is often the beginning of improvements and deeper understanding.
In this particular situation, it started with a test that was hard to write, since it needed to check the output of a console.error
-message that happened when a Redux-thunk rejected a promise…
The setup and mocking was 25-30 lines of code and the check was 1 line. But we couldn’t get it to work.
Taking a few steps back we realized that the code that was being tested consisted mostly of us chaining Redux framework-related code together. When we squinted our eyes we saw two lines of code in the middle of all of this code that performed some business logic. That logic was quite simple and loaded a bunch of strings into a listbox, or default values if failure.
We thought about it and realized that it could be pulled out to a separate function that would be very easy to test. The rest of the framework code could be left to an integration or end-to-end test to verify. As it turns out the Redux framework code wrapped access to a backend API. Failures (promises rejected) would happen if the API was unreachable and then the entire page was unreachable.
We didn’t feel that having a unit test for every branch in the system was worth the effort to write; now and for maintenance going forward.
The end of the story was that we ended up with many fewer lines of code, tests, and, most importantly, a better-refactored solution.
We didn’t get there by chance but used a few principles and thoughts from others to guide us.
That was a few musings after a fruitful discussion. Thank you, Johan.
]]>((((DO SOMETHING!) SMALL) USEFUL) NOW!)
In this post I wanted to unpack, what I think, it means and show you how it can be useful to tackle beasts that you haven’t dared or have the strength to do something about.
I even have a screensaver picture that you can download here
First a few words about Bob Bemer who seems to have been a pretty amazing guy, that very few have mentioned.
Born in 1920 he was at his peak when programming and computing really took off and was involved in a few paramount inventions. He calls himself the “Grandfather of” COBOL (according to Admiral Grace Hopper) and the “Father of” ASCII and put the escape sequences into ASCII (\n\t
for example).
Yes. ASCII. The man invented text, friends. Sit down and listen.
The motto is brilliant in how it is written and what it means. It’s a kind of LISP, as far as I understand:
((((DO SOMETHING!) SMALL) USEFUL) NOW!)
And I unpack it like this (realizing that I might sound like the aliens in the storage box in Men in Black 2):
DO SOMETHING
- this is first and the most important. Let’s just do something rather than be paralyzed by analyzing the situation and just think. Rather DO
.
DO SOMETHING SMALL
- make sure that the thing you do is SMALL
. It’s better than doing something BIG. I promise, a story below
DO SOMETHING, SMALL, USEFUL
- it’s good that the thing that we are doing is USEFUL
, but honestly SMALL
is more important. This channels the inner Woody Zuill in me when he said We spend so much time ensuring that we work on the most important thing when working on something important is enough
. Yes - when it’s SMALL
. For BIG
things priority work is very important.
DO SOMETHING, SMALL, USEFUL, NOW
- in the end, Mr Bemer tells us to get going. NOW
- don’t wait, just DO
Where I’m working right now we have a large legacy code base. Building it takes a very long time, 2 hours ca, and many of the parts that we are building are a bit scary to touch so we have just let it be and live with the fact that the build takes that long.
Having such a slow build is slowing our feedback loop down too much for any agile person’s liking so I asked if there wasn’t something that we could do to make that build faster.
Someone looked into the build and we found a little change that would make our SonarQube static code analysis go a bit faster.
But that is really only taking about 2-3 minutes of the total time. I don’t really see the use.
DO SOMETHING
So we made that change. And the build was a few minutes faster for a few sections. The build now took 1 hour 40 min.
Then, just a few days later someone came up to me and said; we actually played around with parallellizing some of the build steps. But it was a bit messy, so we left it half-done. When asked how much was left they told me a few hours.
Turns out it was a bit more complicated than that. But, they told me, when i looked into parallellizing I thought about getting SSD-disks for our cloud service
.
DO SOMETHING SMALL
That takes about 30 seconds to change but the weekend to be applied. And it shaves of ca 5-10 minutes more per build step - the build now took ca 1 h and 10 minutes.
Another developer told me that he wanted to update the version of a tool that was old. But it will probably not affect the build time
. It did - shave another 5-6 minutes of the build time.
DO SOMETHING SMALL
At the fika we happened to talk about this and realized that there are some parts of the build that we do many times over; one for each combination of language and browser we want to support.
That would make a huge impact if we could change that
, a developer lamented.
But we don't need to support Russian anymore
, a product manager said.
That would be useful since it would make the testing scope smaller too.
DO SOMETHING SMALL USEFUL
When we started to investigate making that change we realized that we are building for 3-4 browsers that we are not supposed to support anymore.
All in all, this promises to take the build time down to around 20-30 minutes in total. (To be perfectly honest that’s a calculation that we made - and I’ll update the post here once we see the results).
The thing here is not that we made good changes (although we did). It’s that we made small changes and started to do something.
It is in doing the work we discover the work we need to do
is another Woody Zuil quote that I love.
To conclude our journey is just one simple example of what can happen if we let people do something. But remember, that advice from Father ASCII:
((((DO SOMETHING!) SMALL) USEFUL) NOW!)
Always assume positive intent
However, I’ve now realized that it’s a bit one-sided and it opens up an unexpected opportunity for building a bad culture. Also, it touches on one of the reasons I never wanted to be a manager.
Let me explain.
Always assume positive intent as a general rule I think is good advice. I’ve seen many conversations that could have benefited from some “hey - maybe they don’t want to hurt me?” at the outset. Just taking a little effort to think that the question comes from someone with good intent, before jumping to conclusions about them wanting to do something bad to me - that’s how I understand “Always assume positive intent”
Interestingly enough, every time I talk to people that have had this saying floating around in their company they always shift uncomfortably in their chair and start to say that it can hinder you from ever addressing any bad things.
And sure you can read it as a declaration of never being sad. BE HAPPY. NOW! And stop complaining!
But that’s not how I’ve seen and used it. For me, it’s more of a try to assume positive intent where you can, it's good for the culture here
.
I have however started to think about the direction of the exhortation - it puts all responsibility of the receiver of the message. Regardless of what and how they say it - assume that they had positive intent
.
A common way to express this is “misdirected good intentions”. It goes like this; someone asks a very rude question, or raises a problem in an aggressive, complaining kind of way:
So I guess no one has thought about that?
Have we dropped the ball on X? Again?
I don't want to play the devil's advocate here, but ...
(see the end of the post for how to meet this)I can then, as the receiver of that message “Always assume positive intent” and think Huh - that was rude and problem-focus, but if I squint my eyes really hard, and look past ... waaaay past what they said, there's some goodness to be dug up from the bottom of that barrel. I'm gonna focus on that and meet this with a positive intent
In fact, I have many times been told that as a leader this is what you have to put up with. We say things like:
I don’t buy it. Not one bit. But I’ve heard it so many times that I’m starting to think that we foster a culture where a manager becomes this person. Hence I don’t want that job. I can be a leader, sure, but never formally; if this is part of the job.
Instead - I think that the sender also has a responsibility for the message being expressed. Sure, you can trust the receiver to assume positive intent
but why make it as hard as possible?
And think about this - everyone else who heard you express your well-intended message in a … harder-to-intrepret-than-needed way, how will they express their message the next time? It will affect us since interactions like this build our culture.
There’s a book in Swedish called Björnstad that has a great quote (hope I get it right):
Culture is not only what we encourage, but also what we accept.
Yesterday a friend said it like this Culture is worst thing we accept
.
Hence, we need to address this. If we want the receiver of a message to Always assume positive intent
I think it’s quite reasonable to also require the same of the sender of messages. There’s a whole book on this subject - The No Asshole Rule and anything you can read about how to give constructive feedback will help.
When someone, the receiver OR sender, seems to struggle to see the positive intention we could ask them why. Typically there are other things behind behaviour like that - maybe they need help? Assuming a positive stance in that conversation is a good start, to be honest. And to not take that conversation publically…
To sum up I cannot come up with a short, snappy saying like the original but if I were pressed to write something it would be
`Try to assume a positive intent; in both sending and receiving messages. It will help us build a better culture.
If someone (and I have been one of them) brings up the devil’s advocate argument you can tell them the fascinating story about how cardinals got selected to dig up dirt on people being suggested as saints in the Catholic church. That role is called the Devil’s Advocate.
And then just say that as the world looks today the devils don’t need any more advocates. He has plenty. The other side might need your services though…
]]>I often see tickets on (JIRA, we surrendered to its impact, haven’t we?) boards that are called Develop X
, Test X
, or sometimes even Implement backend for X
, or Update database index in table SOMETHING_USED_BY_X
.
The core idea of a user story is that it focuses on some value that we provide to a user. While developing this value we need to do a lot of small steps. For example, consider a report that needs to be created (I’ll leave it for you to make up a So that …, As a …, I want …
` part).
There are a whole bunch of tasks that we need to take there:
Let me ask you this – which of the steps above creates value? The author left time for the audience to ponder. And then he continued: Exactly! None of them. Or all of them together. Or … wait a minute here.
That’s the sad reality of software development – each task contributes to a whole, whose value is not realized until a user uses it in production. Until then the feature, and all its sub-tasks, is truly useless.
But, I’m not denying that those steps are needed. It’s just that our user (you know, the person that pays our bills) don’t really care about those steps. She’s like “Whatever man – just give me the report. Before that, not really interested.” I’ve written about this before in the blog post When was Lars happy.
Conversely, if we focus on the whole, we also automatically focus on the value for the user, in conversations about the ticket. This is something I’ve seen very beneficial throughout the development process.
Mike Burrows puts it well in his Working definition of done
Done means that someone’s need was met.
A task, part of the process of creating that value, is not that interesting and useful to the user (have you ever got a “Thank you” from a user for the great documentation you wrote, or test, or code or specification…?)
If the tickets are called Update the backend API
or Test that parameters are correctly managed
it’s very likely that people do exactly that. Which is good, but also inevitably leads to me focusing on only my job and leaving the rest to someone else:
There – that spec is now written. Developers will figure it out, or ask me I suppose.
There – that was my code done! If there are any bugs in there, I presume the testers will find them.
There – testing is done. I wouldn’t have written it like that, but hey – it works.
Tasks focus our attention on tasks – user stories help us focus on the whole. I’ve yet to meet a team that thinks that Quality is just the testers’ responsibility (even though they are sometimes called Quality Assurance). Not it’s everyone’s responsibility – we together need to work on it.
And you know what, having a tester to bounce ideas off is a great resource when writing both code and specifications. Having a developer next to you when doing testing is not that bad either.
A task is done by someone. The tasks on the board naturally shift our focus to the person doing the task. Which in turn shifts our focus to asking if people have things to do. I’ve already written about not focusing on keeping people busy in standups (https://www.marcusoft.net/2017/03/comments-on-board-practices-7.html) but I can reiterate one point here:
How many people, without anything to do, just put their feet up and wait? I’ve been in the IT industry for over 25 years and I’ve never seen it. What do we do instead? Start something new, of course.
That means that work in process just went up, which both slowed down the process for not only that piece of work, but everything else in the process (there are mathematical proofs for this ) but also made the thing we are working on more complex as we now have more moving parts.
It’s better to do nothing than to start new work. But I’m sure you are smarter than that and can ask:
Can I help someone out, to finish work that is ongoing?
Yes – even if it is something that you usually don’t do. Like developers testing for exampleCan I help to prepare other work for upcoming steps
– like the specification work of the next feature?Can I improve flow in our process, by improving our tools, test suites or reporting capabilities, so that we can move faster in the future?
It’s not important that everyone has something to do – as long as value keeps flowing (<= that statement got me sacked from an assignment)
If we have a lot of tasks on the board, it will get really hard for people around our team to understand what is going on.
How is that report story coming along?
Great – we have updated both the API and the database index.
Instead, we want to talk about the progress of the story itself. Here I think much of the lure of breaking down into tasks stems from. Because then we can say things like 12/25 tasks are completed. In tools like JIRA, this happens automatically.
And let’s use that then – but remember that the value of the story is not connected to the completion of the tasks. And that one task can take considerably longer than another…
Better is to make smaller stories and move them faster across the board. There’s nothing building trust more than delivering value. If you then start to do some statistical analysis of how long stories usually take … pretty soon you will not be asked to report status at all.
Limiting working in process (WIP) is also a great tool to use to hone your focus and work as a team. The value of WIP limits lessens significantly if you have tasks on the board, as tasks focus on parts of what makes the story.
If you need to do 100 tasks to complete a story, I feel sorry for you, but other than that - the user of the feature still doesn’t care.
Long-time CEO of Scania, Leif östling said:
The customer doesn’t care how we are organized.
Exactly - the number of steps in your process is irrelevant to me. I’m using your product.
Have WIP limits for stories – not tasks.
If you think that you need them to remember what to do – create them. Don’t have them on the board, is what I’m saying. They can be part of the stories that we don’t show and just use instead of a notepad next to our computer.
Also, don’t track progress with the tasks, since that shifts focus back to what I talked about above. Use them a little to-do list, if you need.
Don’t have tasks on the board. It will ruin a lot of the good ideas behind kanban, lean, and agile.
It might be hard to shift over, but your flow, value generation, focus on end users and teamwork will improve from doing it.
]]>In these times I’ve noticed many people trying to be helpful and give the following advice:
Be more careful in how you plan your time.
It’s said with the utmost well-meant intentions but it is honestly not helpful. Quite the opposite, in fact.
It’s not helpful because that advice sent the problem right back at me, or whoever said it.
Also, most people I know finding themselves in this situation have already bent over backwards trying to plan their time better, trying to squeeze some more slots of time out of their day.
When in fact the problem is that there are too many things to do. Plain and simple. You have work for 2 people but you are only one person. There are 12 projects that requires your attention before the week is over, but you will only manage 3.
In this situation I think a better help would have been compassion and listening, if you cannot solve the problem. I know of at least 1 person where this recurring advice tipped her over the edge.
Imagine being super stressed from too many things that you feel people want you to do. Ask advice and then get an advice saying that you are bad in keeping track of your time. Pretty horrible, once you think about it.
The next time someone asks me this, and I cannot do something to help the situation I will at least listen.
I want to emphasize that no person I’ve heard giving this advice did it out of malice. Instead they wanted to help. Maybe they felt that they couldn’t do anything about the situation, or they didn’t realize how much was already being done to try to remedy the situation. Or how much work is being pushed to the person asking the advice.
It could also be that the person being asked the advice has no power to fix the problem.
In any case - realizing that pushing the problem back to the advice seeking is less than non-helpful. You are adding to their burden. In fact, just being silent would be more helpful.
But as the people giving the advice wants to be helpful, a compassionate listening is well within the reach of what we can do I think.
Before I suggest some potential solutions, let’s talk about why this situation happens. I think it is by organizational design in many cases. Mostly also well-intended, sometimes out of malice but rarely noticed.
I have actually met, more than one, manager that thought that the best way to get people to do their best was to push more work than they would manage to them. Then at least some of it would get done
was the reasoning.
Just about every organizations I’ve ever worked in have more things to do than can possible fit in the available time and resources. Which makes this problem a prioritization problem.
Well, actually it could also be made into a quality problem. We could do all things with bad quality and try to squeeze them into the available time. There are several problems with this. First bad quality work tends to create more work as it comes back to us later.
Secondly, say that you got away with it and the bad quality was good enough - now people will think that workload you had is reasonable. Let's see... they had 12 projects going on, at the same time, last month - so I guess 13 would not be too far of a stretch.
No that will not fly. We will have to prioritize.
Prioritize, if it is to be useful, means not doing things. Down-prioritizing something means not doing that thing. It also means that we will look at that thing again later and see if we should do it. But right now - we are not doing it.
Herein lies the crux of prioritization; each thing on the todo-list is important to someone. That means that we will not be able to do some of these important things. That is important to realize; we will not do important things.
It might seem unnecessary to point out, but most places I’ve worked (and some people I’ve met), seems to think that down-prioritizing means that I do it with a little bit less effort. No! Not prioritizing means not doing. Very important distinction.
Let’s get to some advice. And the advice I have is very simple to state, but can be very hard to do. Here it is:
Do less things.
Simple don’t do all the things. And when asking for advice ask this:
Which of these things would you not do?
(or if you ask your boss)
Can you help me to prioritize these items, so that I know which ones not to do right now? Please.
Most people that I have met that have too much to do are getting a lot of things done. They have just reached their limits. And that’s when they break. Because they have been on that limit (or beyond it) for quite sometime.
And that’s when people around them are surprised. Because until then, it worked. Right? They did things and it worked just fine.
This is because people around you don’t know how you feel unless you tell them. And that is why Drop the ball is a good advice. Stop doing somethings. Just do not do it. If you already have done that, drop some more balls.
You will soon notice for who that was important. That person can now help you to prioritize:
Yes - I didn’t do X. Because I had A, B, C, D, E, F, G, H and I. And J. And all of those people told me that it was important. But if you think X is more important I’m happy to stop doing all of them. Could you just go over to those people that cares about A-J and tell them that I’m stopping with that?
Or, a little bit less confrontational:
Yes - I didn’t do X. It simple didn’t fit in my workload. Can you make it smaller?
Yes - I didn’t do X. It simple didn’t fit in my workload. Is there parts of it that someone else can do?
People that has too many things to do are not helped by getting told that they need to plan better. That just causes more work for them.
Instead help them to drop things that are not important right now. Yes, drop things. If we don’t stop doing things people around us will not know about the situation.
]]>The title of the video was “PrivateGPT 2.0 - FULLY LOCAL Chat With Docs”
It was both very simple to setup and also a few stumbling blocks. But in the end I could have conversations in English (and broken Swedish) about how to build data pipelines, the Scling-way, by feeding the AI our documentation files.
This is all running locally on my machine without any keys to a third party service.
I’m going to split this post into two parts; first a description on what I want to and why, then a section on how I set it up
ChatGPT (or other LLM based chats) has taken the world with storm and the word “paradigm-shift” feels appropriate to describe where are at. But there is a need that I think I’m not alone in having; I want it to know what we know in the company.
Here’s my example; I’m a fairly new developer on a platform that contains loads of code, opinions and recommendations that I need to scan the code to know. Or ask a colleague, which gets annoying for both me and people around me after awhile. It’s sometimes simple questions like have anyone else downloaded FTPs in Scala in our platform?
, but can also be a bit more vague I've never created an integration test on our platform before, can you guide me?
.
To do this I would have create a GPT chat that knows what we know, and hold the same opinions as we when it comes to preferring one solution over another. I want a Scling-Chat-bot.
Turns out that creating one is suprisingly simple - using the Private GPT that the video above showed us.
I’m no expert in this field, and the following description is my layman understanding of it… but:
PrivateGPT is a webserver (including an API) that runs locally on your machine (you can run it in a docker, but that seems to be not recommended due to performance / hardware virtualization issues… layman, told you). When setting up your local PrivateGPT server you feed it an LLM, that you download. This is important as it means that you can use whatever LLM you want. For example the GPT-SW3 Swedish model.
In my understanding the LLM model is what gives PrivateGPT the model to hold conversations, to help it to understand languages, in short. LLM - large language model. GPT - Generative Pre-training Transformer. But that is general knowledge and doesn’t know things about our specific world; the Scling-platform, our products, use cases from our support etc.
What makes PrivateGPT very interesting is that you can easily ingest documents (in MANY formats) to teach it that missing knowledge. Before I ingest the Scling user documentation into PrivateGPT it would recommend any old framework to parse Excel files in Python. After the ingestion it would make recommendations based on the Scling documentation. (It should be emphasized the the Scling documentation is really good and have recommendations for these things in the form of Architectural decisions :))
And here I’m sure that you can come up with many more examples;
But how to do it - let’s see. It’s not hard, but had a few caveats too.
Everything I write from this point on is what I learned from following the installation page of PrivateGPT. If this doesn’t work, you should probably go that page and follow their instructions instead.
At the end I will put all of these commands in one file, which will look much like their quick start. However, not reading through and understanding the individual steps will cause problems later. Trust me on this…
To use PrivateGPT you need:
curl https://pyenv.run | bash
which guided me rightStart by cloning the repository to get the PrivateGPT locally. This is a key feature of using PrivateGPT - you run it locally.
git clone https://github.com/imartinez/privateGPT
cd privateGPT
Now use pyenv to install and use the correct version of Python and activate the environment:
pyenv install 3.11
pyenv local 3.11
PrivateGPT uses a tool call Poetry to manage dependencies and run commands like start and ingestion. We need to install that into the PyEnv virtual environment. This is how to do that:
pip install --upgrade pip poetry
Once that Poetry is installed you can start PrivateGPT using the following command, that will install all dependencies. This command will install all dependencies to run PrivateGPT locally:
poetry install --with ui,local
Here’s a step that I don’t know so much about and that I also had to battle a bit. PrivateGPT GPU support via a C++ compiler… I don’t even want to know. Long-story short, this connection or usage of the GPU is done using using llama-cpp-python
, which needs to be installed like the following.
CMAKE_ARGS="-DLLAMA_METAL=on" pip install --force-reinstall --no-cache-dir "llama-cpp-python==0.1.59"
However, I had to use an older version, hence the ==0.1.59
at the end. Without that addition the installation failed for me. Read about this step here.
Start the application using make run
, but make sure that you pick up the local
settings from the setting.yaml
-file using :
PGPT_PROFILES=local make run
You can use another profile by setting the PGPT_PROFILES
variable to another name, if you wanted to
You can now see the application running at http://localhost:8001. But it’s using a mock LLM, so once you verified that it works, shut it down again (CTRL+C) and continue
Let’s download a LLM and the absolutely easiest way to do that is to use PrivateGPTs recommended settings, through the setup script:
poetry run python scripts/setup
This command takes considerable time, but it also downloads a 5 GB file to your disk - the LLM
You could now have ChatGPT like conversations with PrivateGPT, but let’s ingest some private data into it, before we try it out.
Ingestion can be done in the UI of the PrivateGPT web but you can also do it from the command line (or write your own code that uses the /ingest/
- API)
Here’s how to ingest all documents (in many formats) from a certain folder:
make ingest /path/to/folder -- --log-file /path/to/log/file.log
(There’s a --watch
flag that I didn’t use. First it caused some problems with the running web server, and secondly I think that these updates are probably better done offline.)
This takes some time, as the ingested content needs to be tokenized in a format that PrivateGPT can understand. It took me about 1 min for 3 mb markdown.
Done You can now start the application again (make run
)
When playing around with this I found the wipe
command very useful. All the ingested data ends up in a folder called local_data
. Simply deleting it works, but causes some weirdness in the UI - PrivateGPT has a command for this, so it’s better to use it:
make wipe
Here’s a single command that sets up a PrivateGPT installation, with parameters for location of the installation and the folder of documents to ingest.
If the file was save to privateGPTSetup.sh
you can call it with:
sh privateGPTSetup.sh /privateGPT/Installation /docs/to/ingest
Here’s the script:
#!/bin/bash
echo "Clone Private GPT to $1"
git clone https://github.com/imartinez/privateGPT $1
cd $1
echo "Setting up PyEnv"
pyenv install 3.11
pyenv local 3.11
echo "Installing poetry"
pip install --upgrade pip poetry
echo "Installing PrivateGPT as local installation"
poetry install --with ui,local
CMAKE_ARGS="-DLLAMA_METAL=on" pip install --force-reinstall --no-cache-dir "llama-cpp-python==0.1.59"
echo "Downloading standard LLM using script/setup"
poetry run python scripts/setup
echo "Ingest all files in $2"
make ingest $2
echo "Ingestion done - Starting application"
PGPT_PROFILES=local make run
echo "Application running at http://localhost:8001"
With that you can now setup your own PrivateGPT, that doesn’t require any keys, or to upload documents to a third party, that you can feed documents to give it knowledge of your data.
A brave new world
]]>I have bumped into people that have taken #agile and #lean to other and better places than me, and done more of it than I ever thought. Of course and it’s lovely.
When working at Spotify years back, I bumped into Behnosh, and had one of those experience. A deep and solid understanding of the agile principle and mindset, applying it to all kinds of situations that I hadn’t even experienced.
Yesterday we had a lunch and I’m always interested in hearing how people ended up where they are right now? So I asked how she got started with agile, and the answer floored me, I actually got emotional even.
Well, it was you and your writing, Marcus.
We’re talking 2008, or so. My understanding and musings was very basic at that time. But - Behnosh read it (and, it should be said, many other things :)) and made a career of it. She did that alone, I got to be a small seed of that.
Here’s the thing, at the time I was VERY fortunate in several regards; I worked at Avega where learning was a core theme from the creation of the company (thank you, Markus Torpvret for Elevate and that focus)
Secondly the people that I got to work with there reads like an #agile #lean #kanban hall of fame now. These were all in my close network and we met regularly; Håkan Forss, Joakim Sundén, Christophe Achouiantz, Morgan Ahlström, and many others that inspired me.
We did things together; played lean games, presented, bounced ideas of each other and wrote articles and blog posts. It was the best school I’ve ever been in. I was “worst in the band”, as it’s known - but I learned a lot.
What an amazing opportunity and fortune to be in that context. I had done nothing to deserve that. But I did one thing - I shared. I wrote blog posts (https://www.marcusoft.net has been going since 2006), I presented, listen to others and talked. Now, sometimes that was driven by an urge to learn more - but I also did share a lot. It was my way to document for myself, but I shared my thoughts with other. I did that part.
That people that I didn’t even meet until many years later got use from that now and then made great things of it… Blows my mind.
Share your knowledge. You will learn by doing it. And you know what; you might even affect people you don’t know yet.
Thanks Behnosh for telling me about this!
]]>I’m still in the process of recovering from fatigue syndrome, I realize. I’m doing better but the fact that it’s now been 2 years and I’m still thinking about that every day makes me think that I’ll never get out of it completely, but rather need to learn how to live with it.
Lately, I’ve noticed two things that pull me backward, and I wanted to write down how I get out of it so that I’ve at least written it down once.
The first is more of a reaction than a thought. As I have been open with my depression and how I felt it’s inevitable that people will recommend things to see or read. I love that because it’s done with care for me.
But nowadays every time I read about someone who describes their break-down moment, or depression, or “hitting the wall” - it is if I get to re-live my moments (yes there were two) again. Not as deep and horrible, but still to the point that I every time wonder if I can read on, and always has me in tears.
It is as if that area of emotions is still sore to me. I think that I cannot “touch” it without being pulled in again. And it scares me. A lot.
But to date, I have never been pulled in. The pain often goes away as quickly as it got to me and I feel better getting out of the moment than going in. I just cannot seem to understand that beforehand and find myself avoiding reading, seeing, or even thinking about sad things.
Those stories bring back memories. But it’s just memories - not the real thing.
The second thought is “I’ve already accomplished the best thing I will ever do in my life”.
My big break-down moment came when I was faced with the picture I had of myself, compared with the pictures that I thought others had about me. I consider myself very lucky and privileged to have done what I have done in my life and career. Many of which were things that I would never dream of doing growing up.
In my professional life, people nudged (pushed me a few times) me forward to positions that I didn’t want or was ready for. A few times I got out of it alright and others … well - you probably understand what when my moment happened. I want to emphasize that it was my inability to stop in time that was the problem. I could have declined, but I didn’t because I felt obliged to accept.
When my own ideas about what someone who has 3 wonderful boys, is the occasional soloist on his instrument, an informal leader in close to every group he is in, a public speaker and an author should be - met the reality of … well just me. That’s when things broke down. I couldn’t live up to my expectations.
Now that I’m recovering and understand that I’m putting those requirements on me - another thought comes to me that is scary and sad. Maybe I’ve done the best I will ever do with my life.
Elin and I helped save a hospital in Indonesia. Looking back that might be the best I will do. We have 3 wonderful boys. Maybe nothing will ever trump that? Writing two books? Playing in Albert Hall?
What good is the rest of my life then? (Yeah, it gets pretty dark here. Hold on - we’re soon out of it…)
Well, I can also choose to think like this; I have done those things and I can be proud of them. Much of what I think, do, and am today is based on what I learned from it. And even if they turn out to be the highlights of my life, the second, third, or fourth best might still be around the corner - and that ain’t bad.
Also - I cannot remember knowing before it happened that this was going to be the best I ever did. I just tried to do good. And that’s good enough. I don’t demand more of me.
Your best will happen sometime in your life. You don’t know until you lived your entire life when that was. Doing good is enough and might lead you to a new best.
To be honest I don’t know why I wrote this. It was for me mostly. If you got anything out of it, I’m happy.
For me, many of the problems I had were within me and my thinking. That has one big advantage - I can do something about it. By thinking and reasoning differently. This post was me doing that.
]]>