David Ruttka

I make computers do things

UWP - Data Templates in Resource Dictionaries

| Comments

Lately, I’ve found myself looking into UWP development. Tonight I want to share a problem I ran into and a workaround that I found. I’ve created a repo that shows the step by step progression, and this blog post is meant to provide additional context around that.

Pre-emptive nitpickers note: for the sake of problem reduction, the code in this repo is not overly concerned with patterns, best practices, error checking, and so on

Some Background

In XAML, there’s the concept of a DataTemplate. This allows you to define what UI components or user controls are used when displaying certain objects inside of other content controls. A canonical example is a ListView.

Without Templates

By default, a ListView will just ToString() the object and put it on the screen. You can run this commit, or look at the screenshot below.

A ListView without a DataTemplate

Notice that the ListView says “DataTemplates.RedThing” and “DataTemplates.BlueThing” (i.e., the System.Type of each item) instead of any meaningful representation of the instance properties.

DataTemplate, Binding

In this commit we create a ResourceDictionary1 with a couple of DataTemplate definitions in it. We also merge that ResourceDictionary into the app resources in App.xaml, and create a ThingTemplateSelector to use one or the other based on the type of underlying Thing.

A ListView with a DataTemplate

I’m going to be very careful not to say this looks good, but it proves the technique. We can now let our designers have all the fun polishing the DataTemplate itself, and we’re good to go! Right?

What Are Tests? Baby, Don’t Hurt Me. Don’t Hurt Me. No More!

I have to say that whatever positives there might be with UWP, the unit testing story is not one of them. The vast majority of issues we’ve seen so far have boiled down to testability.

The next commit highlights one such example where our app runs run fine, but our unit test application crashes at startup. The error isn’t what I’d call helpful.

… test discovery output, a la deployment of application, etc., omitted …

A user callback threw an exception. Check the exception stack and inner exception to determine the callback that failed.

========== Run test finished: 0 run (0:00:04.9484062) ==========

If you debug the test to check the stack and inner exception, both are null. You may now flip your tables.

Update 6/30: After some discussion about this post, I realized it could be even clearer. In the original commits, the unit test was a UITestMethod that tested the template selector itself, making it seem like the problem was limited to testing UI concerns. Now, our unit test does nothing other than Assert.IsTrue(true) in a TestMethod. Let us all agree that one should always be able to Assert.IsTrue(true) without one’s unit tests crashing.

The Workaround

Of course, I neither force quit nor flipped tables in real life. I took a breath and decided to push through the pain. What I found was that our test project would not crash if I removed our equivalent of <local:ThingTemplates /> from the App.xaml.

In the final commit, you can see how our custom DataTemplateSelector takes care of instantiating the dictionary it needs itself. One might argue this is actually cleaner anyway. I’m not currently going to argue that point either way. All I’m going to say is, “Now we can put DataTemplates in a ResourceDictionary and still keep our unit tests, too!” That makes me a much happier camper.

1Note: The ResourceDictionary needs a code behind because we’re using x:Bind. This is a new alternative to Binding that lets you take advantage of static types. Leaving aside any debates on the merit of static types themselves, I feel that you might as well use ‘em if you got ‘em. I have not confirmed if the problems in the unit test project would also exist when using {Binding} and eliminating the code behind.

Templated Server Views With Hapi.js

| Comments

Today I realized I’ve overlooked a great feature of Hapi.js, so I figured I’d write a quick post about it.

What I Was Doing

For a server rendered view, I was doing this.

var jade = require('jade');
var fs = require('fs');
var template = jade.compile(fs.readFileSync(pathToTemplate));

// later on
return template(context);

This was fine for what I was doing at the time, since the rendered output was really part of a larger response, and was pretty much the only area in the system where such a thing was being done.

What I Was Doing Today

Instead of having some rendered output as part of a larger API response, I was creating a route that should actually return an HTML view. The above still works fine, but I could see a bit of friction coming in if this project has more views.

  • I would want to set the Content-Type, either through Hapi’s reply.header or reply.type. This could get repetitive.
  • The readFileSync and compile dance could get a bit repetitive or tedious.

What I Do Now

// During the server setup 
server.views({
  engines: { 
    html: require('jade'),
    path: __dirname + '/templates'
  }
});

// Later, in handlers
return reply.view('index', context);

Hapi takes care of reading, compiling, and applying the template as long as it’s in the configured path.

More information about what engines are supported and how to use other options can be found by reading up on Hapi’s vision plugin.

Committed to Good Commits: Atomic, Frequent Commits

| Comments

This is part of a series on good commits.

In this post, we’ll discuss atomicity and frequency. Remember that this series and the talk from which it came is a description of what has worked well for me, not a prescription of what will work well for you. It’s ok to do things differently.

Atomic Commits

By an atomic commit, I mean there’s just one reason for change included in the commit.

This Commit Is Not Atomic

Fix 4, 5, and 99

4 Adjust font sizes and colors
5 Changed the splash screen timeout from 1 sec to 5 sec
99 Implemented the new ruleset for discounts

4 and 5 are pretty small, so why not include them all in one commit? Here are a few reasons to think about.

  1. The commit message is not concise. The actual change description comes in the extended details, not the summary.
  2. None of these can be reverted without reverting the others1.
  3. None of these changes can be cherry-picked without the others2.
  4. This works against making frequent commits, which we’ll discuss shortly.

We can see some parallels with the debugging and troubleshooting concept of only turning one knob at a time. If you change three things and it gets better, you don’t know if it was one of them, a combination of two of them (which two?!), or all three working together. If all three are needed, then of course commit them as one cohesive unit. If not, consider keeping them separate3.

Why Not?

I’ve only heard two common arguments against this, and one is the same “habits” reason we’ve covered in almost every topic in the series.

The other is that the log gets much longer, and that’s certainly true. Whether this is a problem is another matter. I find that it’s often helpful to see a greater number of more granular commits in the log than to see a few gigantic ones. It’s more clear to me what changed when and why, especially since the message are more concise and specific. By the same token, it’s easier to search for a given commit.

There is one more option to keep in mind as you consider what works best for you. With some version control systems and branching strategies, you can have the best of both worlds. When the full history becomes irrelevant as a new feature reaches completion (i.e., in the future, we’ll only care that the feature was implemented, not about each step we took along the way), then you can squash the Work In Progress commits into a smaller set just before you merge them in.

Frequency

If you’re making atomic commits, you’re probably also committing frequently.

It’s like undo/redo, but
– with named states
– across files
– without loss of the undo stack when the IDE or system restarts
– and you can jump back and forth multiple points at a time

It’s like saving your game right before the boss fight

I’ll usually commit each time I make forward progress toward my goal, or whenever I’m about to make a significant change across multiple files. This might mean I get one more test passing, or it might mean I’ve created something that “works” but needs to be refactored. The value in the commit is that no matter how bad I break things during the next step, there is zero effort to put things right. If I find myself down a terrible path, I just reset to HEAD, and I’m safe at home.

Depending on your branching strategy, you might even push your commits frequently so that you can get early feedback (from your peers and/or a build server). Contrast this with waiting until work is done, when feedback will often be withheld or ignored “to avoid the cost of rework”4.

So how frequently?

It depends ;) I certainly don’t think of it in terms of time. I think of it more in terms of progress versus risk. Do you have more value than you had before? Do you want to protect it as a known good state to which you can time travel later? Are you about to experiment with a wild idea or undergo a large refactoring? Consider whether there’s value in giving yourself a checkpoint, especially if your VCS supports squashing it later if you realize later that you didn’t need it.

Why not?

A lot of people worry that this will cause destabilization.

This is a very valid concern if committing means that it affects the whole team instead of just you5. If your commits only live locally until you push them to the server, or if your pushed commits will be isolated in a topic branch, then you’re only affecting yourself.

If the commits (or check-ins) are into a shared mainline, then yes, frequently adding your half-baked work in progress can indeed break builds and impact your team. However, there are ways around breaking your team and benefits to earlier integration. You will have to integrate at some point, and delaying that will only make it harder at the end. Do a check-in dance. Find out earlier what’s going to break, and it’ll be easier to correct your course before getting too far down the wrong path6.

Thoughts?

Again, this is what has worked for me and my teams. It might not be best for everyone. It might not even be best for me…just the best I’ve found yet. Please do share the pros and cons of any alternatives that have worked well for you.


Footnotes

  1. To be clear, you could manually revert them piece by piece by paying careful attention to what changes went with what commit. I’m talking about a quick, automatic revert, as when using git revert.
  2. To cherry-pick or not to cherry-pick is another topic. All I’m saying here is that if you were to cherry-pick, three changes come with that commit.
  3. If you’ve already made several changes before realizing they should be separate, can look at git add -p to selective stage and commit portions of changed files instead of all of them. The danger would be that if you separate things that actually needed to be together, one of the commits might actually be unbuildable. When (if ever) that’s acceptable is another topic.
  4. Transitioning from code reviews when work is considered complete to an ongoing, collaborative discussion as soon as work begins is incredibly powerful.
  5. And your pair(s).
  6. If you are isolating work in topic branches, you’re guarding against destabilization of the mainline, but you’re opening yourself up to the pain of delayed integration. You don’t see conflicts or incompatible semantic changes until you merge. One solution is to regularly incorporate the mainline into your branch (e.g., rebasing or merging master) to see and resolve these problems earlier. Of course, this doesn’t solve the case where Topic A and Topic B are both compatible with master, but not with each other. We will talk more about these tradeoffs in a future post about branching strategies.

What You Think vs. What You Know

| Comments

Me: Have you checked the closet?
5yo: No, I know it’s not there.
Me: But have you checked the closet?
5yo: Nooooo. I know it isn’t there.
Me: I hear you, but I want to know if you’ve actually looked.
(wife comes back with the missing shirt)
Me: Was it in the closet?
Wife: Yep.

As a result, we had a good discussion about the differences between knowing and thinking. It boiled down to these points.

  1. You didn’t know. You thought. There’s a difference.
  2. Not knowing is OK. It’s just one more thing you can learn.
  3. Indeed, smart people don’t know everything. They know there’s more to learn.
  4. Smart people consider helpful suggestions.
  5. Being adamant that you know when you don’t really know can make problems harder to solve.
  6. Just because someone says they know doesn’t mean that they know. Trust, but verify.
  7. Smart people test what they think until they know.
  8. Again, always, always remember that it’s ok to be wrong.

Verbosity Ahead. You could stop reading here and be fine.

Smart Doesn’t Mean You Know Everything

My 5yo is incredibly smart, and there’s no parental bias coloring that statement =) The problem is when her confidence transitions into a need to be the smartest person in the room. She often joins an ongoing conversation with, “Well, actually, it’s (fill in the blank).” Sometimes, she’s technically correct, but is missing the point. Other times, she has drawn an incorrect conclusion, but it’s amusingly logical based on the data that she has. Then, there are the times when she’s just making noise, trying to avoid looking like a person who doesn’t know something. As the old saying goes, it’s usually better to remain silent and appear ignorant than to speak up and remove all doubt.

This isn’t just my five year old. Many professionals (not excluding myself) do this because they confuse being vocal with looking smart, and they conflate being silent with appearing confused or uninformed. Sometimes we even chime in with something that isn’t even relevant, but it’s a data point that proves I Know Thingstm!

The other danger in thinking you know everything is that you lose the appetite to learn and improve. Knowing that you can’t even know how much you don’t know keeps you reading, keeps you experimenting, and keeps you pushing for even better ways to do things. Accepting that you’ve learned all there is to learn keeps you stuck doing whatever you’re doing. You may be the #1 ranked expert at frobbing the wizbangs, while everyone else has realized that wizbangs don’t even need to be frobbed anymore.

Smart People Accept Help

Just as my 5yo wouldn’t check the closet, many professionals refuse to accept a suggestion because they are sure that it’s wrong. As time passes and it becomes the last possible thing to try, it turns out to be the solution.

Consider that your team is probably trying to be helpful, so they probably have reason to believe that the idea is worth a shot. Why not consider what they’re offering? In the worst case, you spend ten seconds opening a closet. In the best case, you have your shirt.

My discussion with a 5yo put it a little something a little like this

Remember that we’re trying to be helpful, not trying to waste your time or patronize1. Listen.

Do Not Present Guesses As Facts

We hear a lot about how interrupting a dev is the worst thing you can do. Let’s consider that there might be something worse. You could send them on a wild goose chase by presenting a guess as if it was a vetted root cause analysis.

Guess liberally, but be clear that you’re guessing. Do not present your guesses as data. – @druttka

What if we took my kid’s confident statement of knowing the shirt wasn’t there at face value? The shirt would still be “lost”. We professionals often present our own guesses as facts. I can think of many times that a bug report came with a full analysis of when it started happening, why it was happening now, and what we needed to change to fix it. After a few hours of tunnel vision2, it was discovered that everything we “knew” was wrong. The timeline was off, the root cause didn’t even exist, and the proposed change was impossible because the code to change didn’t exist either.

The tunnel vision could have been avoided, and time saved, if the briefing had included a simple, humble, “I think”. Then the ideas would have been considered as ideas instead of taken as facts.

Test Assumptions

This brings us to the fundamental truth that we should always test assumptions. We all know the old saying that goes with this one, but we often forget to put it into practice. I’ve been trying to incorporate the following ideas into my thought process.

First, I try to remember to say “I think” if I just think3. If I do have evidence or reasoning, I try to remember to say “because (evidence or reasoning).” This helps the other person take what I’m saying for what it’s worth, not as hard truth. I also ask them to confirm what I’m saying, since I could be wrong.

Second, I add a silent “I think” to what others tell me unless or until they say “because (evidence or reasoning).” Even when they present evidence or reasoning, I try to hold off on accepting it as truth until I can confirm it in the same way I’d expect them to confirm anything I told them.

Third, I’m getting more liberal with asking “How do we know that?” It works well in combination with the point above because it prompts a person to voice their evidence or reasoning. Then we can vet it together. For example, if I sit down to pair with someone who has been investigating a problem, they might say, “We know this is being called.” I will usually be the kind of person who asks how we know it’s called4. If the response is “Because we see this text on a button changing,” I will be the kind of person who asks “Is that the only thing that changes the button text?” I’m aware that this could come across as annoying, but it’s often followed by “Oh…”. The realization is not always directly related, but sending the train of thought down the right track is still a positive outcome.

I guess the tl;dr on all of this is that problems can be more easily solved if we are careful to separate what we know from what we think. All the rest would flow naturally in a healthy, functioning team.


Footnotes

  1. If this is not the case, you might need a new team.
  2. I’ve talked more about tunnel vision in problem solving here and here. Will covers it here.
  3. I was once given advice that “I think” is a bad thing to say. Lacks commitment, looks weak, etc. I have come to respectfully disagree.
  4. Did we log something? Set a breakpoint? Make a change and see it reflected on the next run?

If It’s Good for You, Good for You!

| Comments

tl;dr: We need to stop telling others that they’re doing it wrong just because they’re doing it differently. Different things work for different people; we should at least consider that other paths exist, even if we choose not to take them.

Y’all Be Jokin’ On Me

The other day, I saw this meme:

The instance of the tweet that I saw had about 9001 comments from both sides of the estimation debate that proved the point of the meme itself. My initial reaction was, “Yes! This observation is both humorous and valid. Often, people do indeed respond with great scepticism and dismissal when someone suggests exploring a new methodology!” The replies quickly transformed my feelings into something along the lines of “Stop arguing on Twitter about why everyone needs to do it your way!”

Different Strokes

As I said above, to me it wasn’t really about whether estimates or no estimates is the best way to do things. It was about the way people (not isolated to developers) insist that their way is the only way, and everyone else is an idiot who has never been successful with anything in their lives. It’s about how simply suggesting that an alternate path exists is treated as the incoherent babbling of a mad person.

So many are convinced that what works for them and their team is the one true way, and anyone doing something else must be converted. To such a mind, there is no need to try something else, or to even acknowledge that something else exists. Methodology, language, framework, tabs vs. spaces, and even source control1 somehow become matters of great urgency.

What if we focused instead on the value being delivered and the problems being solved? What if it’s ok to eat mashed potatoes with either a spoon or a fork, as long as the food is making it into the mouth?

Objectivity

There are certainly cases where a new technology, a new methodology, or a different source control solution can yield significant, objectively measurable improvements. There are certainly patterns which can wreak havoc on a system or compromise privacy and security. In such cases, of course we should share that knowledge. However, there are ways to make such sharing effective, and there are ways to ensure that no one takes you seriously.

Ways To Make A Point, Ranked, Edited For Length and To Make A Point

(1) References to case studies, production systems
(2) Side by side demo solutions, real world scenario
(3) Side by side code examples, contrived scenario
(4) Prose describing hypothetical situations

(9001) Popularity contest
(10001) Get really loud
(11001) Get really snarky
(NaN) Call the other person stupid

What If They Still Won’t Listen?!

Well, if we’re going to start a Dark Knight reference, we might as well wrap up with one. Symmetry!

Please do calmly, respectfully highlight of the pitfalls of pattern / framework / methodology X and how Y mitigates said pitfalls. If a person insists on moving forward with X, you’ve done the best you can. It is not your job to change a stubborn, irrational person into a thoughtful, logical one. If the dangers are real, then they’ll eventually be bitten by their willful ignorance, and maybe they’ll want to revisit the discussion. If not, then maybe those dragons you were seeing weren’t such a big deal after all, and you’ve avoided fighting over something irrelevant.

Admittedly, this is a bit more complex if it’s happening within your own team. In that case, if the dragons come, they’re eating you too. Even if they don’t come, there’s a weird air of conflict hanging around. This post was focused more on arguing with people you don’t work with about things that will not impact you. I might cover team dynamics in the future, or you can feel free to share thoughts on that (or anything else) in the comments section below2.


Footnotes:

1: I’m probably definitely guilty of getting a bit evangelical about Git, but honestly, that kind of falls under the “Truly, Objectively Better” bit ;)

2: Just remember to keep the theme of this post in mind when commenting =p

Why Doesn’t Cordova Run Windows Run the 8.1 App?

| Comments

A few years ago, I did a (very intro level) talk on PhoneGap. Recently, I’ve been getting back into Cordova for serious business — wow, have we come a long way =)

One of the things that caused me a bit of friction was that cordova run windows wasn’t working how I expected. It kept trying to run the 8.0 target of the universal app instead of the 8.1 target.

I was working around this by opening the .sln in Visual Studio and explicitly running the 8.1 project. I’d prefer not to do this.

Today I finally took a minute to search for a solution, and I found what I needed to do.

By default build command produces two packages: Windows 8.0 and Windows Phone 8.1. To upgrade Windows package version from 8.0 to 8.1 the following configuration setting must be added to configuration file (config.xml). <preference name='windows-target-version' value='8.1' />

Thanks, Sergey!

A Set of Fresh Eyes

| Comments

The other day, I saw my wife looking at a recipe book and writing in a notebook. She often looks at these books when making meal plans and grocery lists, so I didn’t think much of it. Many minutes later, she was still writing. I looked closer and realized that she was copying recipes by hand. “You know you could just take a picture and email it, right?” The look of realization spread over her face, and we had a laugh. It turns out that she had considered scanning it, but our scanner isn’t a flatbed.

It struck me as another great example of why team communication is so powerful. She had come up with a reasonable solution, but when it didn’t work out, tunnel vision caused her to miss a working alternative. My teammates have often found me in a similar state and gotten me rolling again with a simple “Why don’t you just…?”1 I immediately see the light and can usually see exactly how my initial idea put me on a train of thought that made me miss it.2

Feel free to share some times you remember when a set of fresh eyes was all it took to see a better way.3


Footnotes:

1: I’d like to think that I return the favor from time to time.

2: In addition, I usually facepalm myself.

3: Pairing can make this process continuous, but that’s for another post.

Committed to Good Commits: Work Item Association

| Comments

This is part of a series on good commits.

Ok, Mr. Anderson

Work Item Association

Another way to enhance your commits is to associate them to work items. This can help everyone on the team – present and future – understand what’s going on with the project.

What Are You Doing?

One of the most common problems I see during code reviews is that reviewers do not have a proper context for what they’re reviewing. Things devolve into something on par with copy editing with no feedback on the plot. At best, bad loops or off by one errors get caught, but there are no guarantees that the changes are covering all of the intended acceptance criteria.

What if the author could give the reviewers a pointer to something that explained what they were trying to accomplish? Something that was already on record in a shared system? Something like a work item?

How Did We Fix That?

Another great advantage of associating commits to work items is to jog memories when the ghosts of crushed bugs come back to haunt the team.

Have you ever come across a problem and thought, “This seems familiar. We fixed something like this a while ago. I just can’t remember how…”

Or, “This is a regression of what we fixed a few weeks ago, but I don’t remember exactly what we did to fix it.”

What if you could look at the old bug and know exactly which commits resolved the issue? What if you could just grep a git log for an issue number?

Test Case Generation

It can also help a test team understand what areas are at risk for regression if they know what code is changing. Put commit information on the bug when you resolve it so that they can review the changes.

Common Arguments

  1. “We don’t use issue tracking.”1 In this case, you can use the extended commit message to provide a lot of details of what your change is attempting to do.
  2. “I forgot!” This, like many things in this series, is just a matter of habit. The team can hold each other accountable, or you can find a way to enforce it via policy.
  3. “Takes too long!” Again, like many things in this series, compare it to the time spent on the change itself and to the time that will be wasted later (looking things up, discussing the intent of the code review, etc.). Seconds on hours is blue on black2.

Thoughts?

What do you think? Is adding this extra information to the public record of your project history valuable? If not, why not? If so, what other benefits have I missed? What else can we do to make it more natural for our teams?

1: I will hold my tongue on asking, “Why the hell not?!”

2: Remember that things can be automated. My teams have used server side hooks in the past so that if the message matched, for example, ^GEM:\d+, we’d automatically associate to the Gemini issue. Think about what works best for your team.

High Performance Developers

| Comments Permalink

This is a super good read.

It’s really pretty simple. I can, during that time, pair up with 10 less experienced developers and show them how to find solutions in minutes for things that would have taken them hours or days. I can make myself available to answer their questions. I can intervene at the point where they’d have thrown up their arms in frustration and despair and spent the rest of the day reading buzz feed and cracked. I can clear obstacles from their paths and help them get things done. I can get them excited about programming and enjoying their jobs.

If I were a high performing developer, but I did none of that, and just used those less experienced or less engaged developers as a prop to make the claim that I’m in some kind of elite club of “10x” people, the opportunity cost of my attitude would more than offset my skill from an industry perspective. If, instead, I help them up their game and overcome hurdles, then perhaps that 10x designation might actually mean something.

Are You Really Blocked?

| Comments

Let’s Talk About Cake

Imagine that you have been asked to make a pineapple upside down cake, as pictured below.

Think about what needs to happen here. Prepare the batter, perhaps from a boxed cake mix. Slice up some pineapples, reserving some juice for the aforementioned batter. Bake it. Flip it upside down. Put cherries on top of the finished product.

That’s a lot of stuff to do. You’ve got the pineapple. You’ve got a proper pan and all the appropriate kitchen utensils. You’ve got the oven. Cake mix? Check.

You do not yet have the cherries.

BLOCKED

It would be incredibly inefficient (and probably a play at procrastination, if not laziness) to halt production of the cake because you don’t have the cherries.

  • You could start the cake and go buy the cherries while it baked.
  • If you feel that’s unsafe, or the time in the oven is not enough to make the run, you could go get them right now.
  • You could call your roommate who is currently at the grocery store and tell them to bring the cherries.
  • You could go get the cherries after the baking is complete and the cake is cooling.

In short, possession of the cherries is not a prerequisite to get started. However, in the course of my career, I’ve seen these reactions:

  • Use it as an excuse to be blocked. Go poll Reddit until the cherry team delivers the cherries.
  • Claim to be blocked in good faith. Defer this high priority task until later, but pick up another low priority task in the meantime.
  • Go ahead and make the cake. The cherries don’t come until last, anyway1.

Push Through The Blockers

Everyone in Seattle knows about Marshawn Lynch’s 60+ yard playoff run, but I think this one is more relevant to the current discussion:

Notice how the announcers call the play over. “Lynch not gonna get anything.” He could have thought the same thing, dropped, and gone to get some water. Instead, he found a way through the blockers and moved the ball – moved his team – forward.

This Is Not Cake Or Football

Correct! Which means you don’t have to go to the grocery store or keep your legs moving through a wall of defenders. You have a vast array of resources and tools — both inside and outside your team — that allow you to clear a path.

  • Service you depend on goes down? Fiddle it. Stub it. Introduce an early return of hardcoded data before the call is made.
  • Error you’ve never seen before? Go to Stack Overflow. Talk to your team.
  • UX Design incomplete? Implement non-UX logic. Create independent views that put unstyled data on the screen. Add styles and navigation affordances later.

We could go on.

When Truly Blocked

  • If there’s a real blocker that cannot be worked around, pick up the next task you can handle.
  • If there’s a real blocker for every task that you know how to handle, then pick one up that drives you to learn something new.
  • If there’s a real blocker for every task across the entire project, your organization has bigger problems.

The point is that there’s usually a way to move your team forward. You might not always see the way, but that’s what talking to your team if for =).

What’s your favorite story about making progress despite something that others called a blocker?

1 – Software doesn’t go stale or spoil as fast as cake. Use caution if attempting with real cake.

Cake image used under CC BY 2.0, Kimbery Vardeman