tag:blogger.com,1999:blog-27969100345663539892024-03-19T03:01:44.978-05:00Szalapski.comSoftware development, .NET, and morePatrickhttp://www.blogger.com/profile/10070636843509784651noreply@blogger.comBlogger39125tag:blogger.com,1999:blog-2796910034566353989.post-38340959013463864802022-06-22T09:28:00.001-05:002022-06-22T09:28:58.184-05:00Catch me presenting on Blazor at Minnesota Developers Conference 2022 on June 22 in Minneapolis<p><a href="https://mdc.ilmservice.com/sessions/#3175">I'll be presenting at the Minnesota Developers Conference, June 22, 2022 in Minneapolis on Practical Blazor</a>. I hope you can come and introduce yourself!</p><p>Also presenting will be some of my favorites, including Kamran Ayub with a fantastic approach to personal finance, Erik Onarheim on building a game engine in JavaScript, and Robert Boedigheimer on Regular Expressions in .NET. So good to finally get back to an in-person conference!</p><p>EDIT: That's today! <a href="https://www.slideshare.net/PatrickSzalapskiCSP/mdc-2022-practical-blazorpptx">Here's my slide deck for reference</a>.<br /></p>Patrickhttp://www.blogger.com/profile/10070636843509784651noreply@blogger.com0tag:blogger.com,1999:blog-2796910034566353989.post-58415444899292704492022-03-25T11:32:00.001-05:002022-03-25T11:32:50.839-05:00Avoiding unnecessary rendering in Blazor<p>In my previous post, <a href="http://www.szalapski.com/2022/03/is-your-blazor-site-rendering-thousands.html">I laid out the fundamentals of what can cause a Blazor site to be slow due to re-rendering</a>. By far the most common cause of such slowness is when hundreds or thousands of components all need to re-render multiple times based on changes to parameters or explicit calls to <code>StateHasChanged()</code>. Often it is the case that only a few of the component instances in question truly need to re-render, but Blazor has no way to avoid such re-rendering, so it is up to the developer to worry about this concern whenever dealing with a proliferation of component instances.</p>
<h3 id="the-parent-component">The parent component</h3>
<p>This potential slowness is often manifest in lists, grids, or tables where the content of each item is non-trivial. Imagine a UI with a grid of product images: a <strong>ProductGrid.razor</strong> something like…</p>
<pre class=" language-csharp"><code class="prism language-csharp"><span class="token operator"><</span>button @onclick<span class="token operator">=</span><span class="token string">"Add"</span><span class="token operator">></span>Add <span class="token keyword">new</span> <span class="token class-name">to</span> top<span class="token operator"><</span><span class="token operator">/</span>button<span class="token operator">></span>
<span class="token operator"><</span>ul<span class="token operator">></span>
<span class="token operator"><</span>Virtualize Context<span class="token operator">=</span><span class="token string">"productViewModel"</span> Items<span class="token operator">=</span><span class="token string">"Products"</span><span class="token operator">></span>
<span class="token operator"><</span>ProductTile Product<span class="token operator">=</span><span class="token string">"productViewModel"</span> @key<span class="token operator">=</span><span class="token string">"productViewModel.Code"</span> <span class="token operator">/</span><span class="token operator">></span>
<span class="token operator"><</span><span class="token operator">/</span>Virtualize<span class="token operator">></span>
<span class="token operator"><</span><span class="token operator">/</span>ul<span class="token operator">></span>
@code <span class="token punctuation">{</span>
<span class="token punctuation">[</span>Parameter<span class="token punctuation">,</span> EditorRequired<span class="token punctuation">]</span>
<span class="token keyword">public</span> List<span class="token operator"><</span>ProductViewModel<span class="token operator">></span> Products <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token keyword">null</span><span class="token operator">!</span><span class="token punctuation">;</span>
<span class="token keyword">private</span> <span class="token keyword">void</span> <span class="token function">Add</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">></span> Products<span class="token punctuation">.</span><span class="token function">Insert</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token keyword">new</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre>
<p>Note here we have used the Virtualize component to avoid rendering components for items in the List that aren’t currently shown (due to scrolling). We are sure also to <a href="https://docs.microsoft.com/en-us/aspnet/core/blazor/components/#use-key-to-control-the-preservation-of-elements-and-components">use a <code>@key</code>attribute on the root child element or component inside Virtualize</a>.</p>
<p>So this parent component seems pretty simple and already optimized using Virtualize; how could it be a problem?</p>
<h3 id="narrow-edits-can-cause-wide-re-renders">Narrow edits can cause wide re-renders</h3>
<p>Note there is a button to add a new empty product. When this button is clicked, Blazor re-renders the home component, which means every child component will re-render as well. In our case, this probably means our ProductGrid and the 50+ shown ProductTiles, and suppose each of which has a dozen small components it shows and thus has to rerender. So that’s easily 770+ rerenders needed just to show the new item.<br>
Ideally, when clicking the add button, there would only be a re-render of the ProductGrid, exactly one ProductTile (the new one), and all the small components (but only those in that one instance): thus 14 re-renders.</p>
<p>The reason each of the ProductTiles has to rerender is that its parent is rerendering, and it takes a ProductTileViewModel (one of our domain classes) as a parameter. Blazor cannot determine that nothing has changed in the fifty shown products, as it cannot interrogate all the contents of ProductTileViewModel and compare them to the previous value–remember, <a href="https://docs.microsoft.com/en-us/aspnet/core/blazor/performance#avoid-unnecessary-rendering-of-component-subtrees">Blazor automatically avoids rerendering based on values only when all parameters are of certain well-known immutable or primitive types</a>.</p>
<p>The main way to address this in the ProductTile component is to override OnParametersSet to detect to store the previous values for every part of the parameter that affects the displayed state, and override <code>ShouldRender</code> on the ProductTile component so that it will return false when the previous values are all the same as the current values.</p>
<p>But such logic comparing “previous to current” would have to be repeated in every component, yet customized for each one to ensure you are tracking all the properties you care about. I’ll avoid showing you the example code for this since it is not the way I recommend doing it.</p>
<h3 id="avoiding-the-re-renders">Avoiding the re-renders</h3>
<p>So here’s where my Nuget package <a href="https://www.nuget.org/packages/Sz.BlazorRerenderReducers">Sz.BlazorRenderReducers</a> comes into play. Using its DisplayHashRerenderComponentBase and implementing the <code>protected override string DisplayHash</code> get-only property, I can simply return a string that is unique for each display state that is possible; usually, this is just going to be a string that contains the value of every property that affects the display state in any way.</p>
<p>This has the benefit of reducing the code required to finely control re-renderings to two lines of code in most cases: one line to <strong>inherit from DisplayHashRerenderComponentBase</strong> and one line to override the <strong>DisplayHash</strong> getter. So, such a <strong>ProductTile.razor</strong> will look something like:</p>
<pre class=" language-csharp"><code class="prism language-csharp">@<span class="token keyword">using</span> Sz<span class="token punctuation">.</span>BlazorRerenderReducers
@inherits DisplayHashRerenderComponentBase
<span class="token operator"><</span>li <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"product-tile"</span><span class="token operator">></span>
<span class="token operator"><</span>ProductImage Product<span class="token operator">=</span><span class="token string">"@Product"</span> <span class="token operator">/</span><span class="token operator">></span>
<span class="token operator"><</span>div<span class="token operator">></span>
@<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>Product<span class="token punctuation">.</span>IsInStock<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token operator"><</span>OutOfStockBadge <span class="token operator">/</span><span class="token operator">></span>
<span class="token punctuation">}</span>
<span class="token operator"><</span>ProductCode Product<span class="token operator">=</span><span class="token string">"@Product"</span> <span class="token operator">/</span><span class="token operator">></span>
<span class="token operator"><</span>ProductPriceTag Product<span class="token operator">=</span><span class="token string">"@Product"</span> <span class="token operator">/</span><span class="token operator">></span>
<span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span>
<span class="token operator"><</span><span class="token operator">/</span>li<span class="token operator">></span>
@code <span class="token punctuation">{</span>
<span class="token punctuation">[</span>Parameter<span class="token punctuation">,</span> EditorRequired<span class="token punctuation">]</span>
<span class="token keyword">public</span> ProductViewModel Product <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token keyword">null</span><span class="token operator">!</span><span class="token punctuation">;</span>
<span class="token comment">// when this value is unchanged, supresses unnecessary re-rendering</span>
<span class="token keyword">protected</span> <span class="token keyword">override</span> <span class="token keyword">string</span><span class="token operator">?</span> <span class="token function">GetDisplayHash</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">></span>
$<span class="token string">"{Product.Code};{Product.Price};{Product.IsInStock}"</span><span class="token punctuation">;</span>
<span class="token comment">// write a console line on every render</span>
<span class="token keyword">protected</span> <span class="token keyword">override</span> <span class="token keyword">void</span> <span class="token function">OnAfterRender</span><span class="token punctuation">(</span><span class="token keyword">bool</span> _<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">></span>
Console<span class="token punctuation">.</span><span class="token function">WriteLine</span><span class="token punctuation">(</span>$<span class="token string">"Rendered {GetType()}"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre>
<p>Here, the DisplayHash get-only property can return anything that is uniquely determined by the data that affects what could be rendered by the component. True to its name, it is a hash for the display of the component. In this example, I’ve just shown a simple concatenation of the values that influence the display, but you could have any function here you’d like – perhaps using hash functions implemented on the parameters themselves, or serializing them using System.Text.Json, but I have found often a interpolated string is simplest and easiest to understand. The base component DisplayHashRerenderComponentBase will then properly store the previous and current values of this hash, and ShouldRender will return true or false appropriately, removing the need to reimplement such custom ShouldRender code in each component.</p>
<h4 id="drawbacks">Drawbacks</h4>
<p>Of course, one drawback to this approach is that it requires that all such components must inherit from DisplayHashRerenderComponentBase, thus removing the possibility of inheriting from any other base component, abstract or otherwise (unless of course you can in turn make that base component inherit from DisplayHashRerenderComponentBase). Fortunately, I have not seen much need to inherit a component from anything other than ComponentBase (which DisplayHashRerenderComponentBase itself inherits from), so this has not yet been an issue for me.</p>
<p>If you’ve followed along to this point, perhaps you can imagine the bigger drawback to this approach: it requires that the developer <strong>specify every potential source of changes</strong> to the display state. In effect, you have to “register” (by including in the DisplayHash getter logic) every property that might affect the GUI in each component, <strong>including its children components</strong> (after all. if a parent component avoids re-rendering, <a href="https://stackoverflow.com/questions/68687308/is-there-any-way-to-disallow-a-component-from-rendering-using-shouldrender-ye">there’s nothing to cause its children to re-render either</a>). As you can imagine, this can lead to confusion as to why a component isn’t re-rendering when it needs to–sometimes parameter properties are used but forgotten to be added to the DisplayHash. No error results and no cue is possible to tell the developer that they forgot to tweak the DisplayHash because of a new binding or some other new code indirectly affecting a bound reference.</p>
<p>However, even this latter drawback seems lower-risk than the frequent errors and confusion that results from manually making every component have a ShouldRender method that would itself need to take into account every relevant property and also would need to track the current and old values of each such property in order to determine if a render is necessary. So <strong>this technique seems to be a distinct improvement</strong> over having such nuanced and perhaps repetitive (though custom) ShouldRender overrides in many components.</p>
<h3 id="demo">Demo</h3>
<p>So <a href="https://szalapski.github.io/BlazorRerenderReducers/product-grid-demo">try it now on this demo page</a>. To the code from above, I’ve added a toggle checkbox at the top to enable and disable the technique. We can open the console an d add an item, you’ll see a console line was written on each ProductTile render. With GetDisplayHash implemented, we’ve reduced the number of ProductTile renders from dozens to one–but don’t forget the child components that are unchanged: <strong>we’ve reduced the total number of components that render from several hundred to five</strong>. In my experience, this often cuts the “lag” after the user clicks the button from a few seconds to imperceptibly fast. On apps where users do lots of navigation or small manipulations, lag of a few seconds can kill your perceived performance, so this is a big win.</p>
<p>So if you have (or suspect you have) hundreds or thousands of unnecessary renders happening on many changes, give <a href="https://www.nuget.org/packages/Sz.BlazorRerenderReducers">Sz.BlazorRerenderReducers</a> a try to help drastically reduce the re-rendering that is needed. You can read more on <a href="https://github.com/szalapski/BlazorRerenderReducers">the BlazorRerenderReducers project GitHub page</a> and leave an issue with any concerns or questions, or just reply to this post.</p>
Patrickhttp://www.blogger.com/profile/10070636843509784651noreply@blogger.com1tag:blogger.com,1999:blog-2796910034566353989.post-52148260115431578562022-03-16T19:00:00.005-05:002022-03-24T21:46:51.790-05:00Is your Blazor site rendering thousands of times?<p>When you think about it, data-bound real-time DOM updates are weird. We update values; then other code that we didn’t call knows somehow to call our code, then our libraries update the DOM based on those results. In Blazor, React, and Vue, it all just seems to work–but <strong>sometimes there is a nagging slowness to sites that bites our users on every click</strong>. What causes this? Usually, it is slowness in <strong>rendering</strong>.</p>
<p>This is a Blazor post, but first let me quickly contrast Blazor against the JavaScript framework/libraries React and Vue, which both have a robust “reactivity” implementation. In Vue, references for data binding are exposed in each component’s data and computed properties. Then, the magic of Vue automatically injects code using JavaScript’s prototype system so that on every set, code is run that causes every relevant getter to rerun, thus causing Vue’s internal representation of the DOM (called the “virtual DOM”) to update itself from the relevant data in memory. This is automatically followed by the internal code that updates the real DOM from the virtual DOM and then allows the browser to repaint the screen. This is actually quite elegant and it is all hidden from the developer. React does something very similar.</p>
<p>But in Blazor, there is no setter injection, as C# does not provide this capability natively. So instead, <a href="https://docs.microsoft.com/en-us/aspnet/core/blazor/components/lifecycle">the Blazor component base classes internally rely on a simpler but less elegant approach</a>: anytime <a href="https://docs.microsoft.com/en-us/aspnet/core/blazor/components/rendering?view=aspnetcore-6.0#rendering-conventions-for-componentbase">the parameters to a component change</a>, all the properties and methods that are bound in that component, <em>and recursively in all its child components</em>, will run. Since ideally the parameters to a component uniquely determine the values used therein and therefore determine the DOM for that component, this is seemingly good enough: everything updates immediately. Microsoft’s Blazor docs call this process “rendering” the component. We consider “rendering” in Blazor not to include the time it takes for the browser to paint this DOM, which is seldom (but not never) significant.</p>
<p>However, this can result in at least two pitfalls. First, the <strong>time to render</strong> (again, think “run C# code for my component”) can indeed be significant. In theory, I thought this would not be a problem; after all, hypothetical C# code run via the CLR (for example, in a unit test or command-line app) would take nanoseconds, and even if there were thousands of components on a page, we’re still talking microseconds here. The problem with this theory is that Blazor code run via WebAssembly isn’t run via the CLR, and so the time it takes a unit test to run a fragment of your component’s code is not the same as it will take a browser to run it. In fact, this difference can be small or it can be 100x or 1000x, and there isn’t always a good way to predict where this slowness will come. (In particular, I’ve noticed that <a href="https://github.com/dotnet/runtime/issues/40386">deserializing is slow</a>.) However, there is <a href="https://github.com/dotnet/aspnetcore/issues/26850">no simple way to profile Blazor WebAssembly yet</a>, so it can be tough to isolate this kind of rendering slowness.</p>
<p>Secondly, when properties of parameters may have changed but didn’t, Blazor will <strong>unnecessarily re-render</strong> that component and all its children. This often causes lots of unnecessary re-renders; for example, consider when a property is set to the same value it already has. Blazor cannot look within user-defined objects to see that nothing has changed, so it presumes that something may have changed and it triggers a re-render. A corollary to this: unnecessary child re-renders will happen when a parent component re-renders even when the parameters to the child haven’t changed. Imagine updating the title of a large table when none of the cells have changed. C# can’t tell if this might have impacted a child, so it plays it safe by re-rendering all children anytime there is a chance that changed data affects the DOM rendered by the children.</p>
<p>So why is everybody having such a great time with Blazor WebAssembly and seemingly not complaining of these re-renders? Thankfully, Microsoft already has reduced the impact of these re-renders as much as can be expected. First, <strong>Blazor is pretty dang fast</strong>. Your site may be doing hundreds or thousands of re-renders in one second without you realizing it. Perhaps it’s bad, but just not bad enough for you to have noticed. Just add the following…</p>
<pre><code>protected override OnAfterRender() =>
Console.WriteLine($"Rendered {GetType()}");
</code></pre>
<p>…in all relevant components and see how much they re-render. You might be surprised at all the plate-spinning going on under the hood. Paradoxically, the speed of Blazor WebAssembly masks the theoretical slowness, and this theoretical slowness tends to grow as development of a site progresses till it becomes actual slowness as experienced by users.</p>
<p>Also mitigating re-render slowness is that, if a component’s parameters are entirely known primitive types (such as bool, int, decimal, string, DateTime, and Guid), then <strong>Blazor will cut off the re-rendering when the values haven’t changed</strong>. So, if you can make a component depend on a few strings or bools rather than one instance of a user-defined type (be it a class, struct, or record), do so.</p>
<p>Another good practice to mitigate the re-rendering penalty is to ensure that rendering or parameter changing does not repeatedly cause loading data from a web service or any other asynchronous operation. Structure your C# component code so that data is loaded only when detecting a change that requires it–often, when the previous value differs from the current value–rather than simply loading that data every time in <code>OnParametersSetAsync</code> or <code>OnAfterRenderAsync</code>. Also, do not bind the invocation of any such asynchronous method directly to a displayed element (e.g. <code><div>@(GetNameFromServer())</div></code>); instead, bind to a property and some other event should invoke the method call to populate that property, usually on user action or based on conditional logic within a lifecycle event handler. In short, <strong>assume your component will be re-rendered many thousands of times, and the mere act of re-rendering should not do any heavy lifting</strong> except when something relevant has changed. Do not make components that require parent components to carefully limit re-rendering.</p>
<p>Is frequent re-rendering causing a long list, table, or other repeated components to bog you down? Most loops in your Blazor markup can be replaced by <a href="https://docs.microsoft.com/en-us/aspnet/core/blazor/components/virtualization">instances of the <code><Virtualize></code> component</a>. This component is a godsend: it will render only the iterations needed to provide a good user experience. The component often is a no-brainer drop-in replacement for a <code>@foreach</code> loop, so learn it and use it often.</p>
<p>Finally, Blazor provides the <code>protected override bool ShouldRender()</code> method. If you have some logic where you can decide when a rerender is necessary, just implement <code>ShouldRender</code> to return false in the cases. Don’t worry about coding the first render as a special case: <code>ShouldRender</code> doesn’t get called on the very first render of a component, so you don’t need to add code to cover the first render case. (Also note that if ShouldRender returns false, <a href="https://stackoverflow.com/questions/68687308/is-there-any-way-to-disallow-a-component-from-rendering-using-shouldrender-ye">then child components will also not be rerendered</a>.)</p>
<p>Even with all these partial solutions, you may well find yourself in the situation where you’ve optimized all you can and many components are still re-rendering too often. How do you reduce unnecessary renders when dealing with non-primitive parameters without adding lots of non-intuitive, custom complexity in <code>ShouldRender</code>? I’ll have <a href="http://www.szalapski.com/2022/03/avoiding-unnecessary-rendering-in-blazor.html">a solution for this in my next post</a>.</p>
Patrickhttp://www.blogger.com/profile/10070636843509784651noreply@blogger.com0tag:blogger.com,1999:blog-2796910034566353989.post-5257223327225313482020-12-13T15:50:00.003-06:002020-12-13T15:50:21.888-06:00C# ascendant: Nuget packages to try (and some that are no-brainers)<p> </p><div class="ms-blog-postBody"><div class="ms-rtestate-field" dir=""><div class="ExternalClassCE81C883DAB440D39457EBB5A530C437"><p>I
feel like with the advent of Blazor, the power, usefulness,
and ubiquity of C# and .NET in general has kicked up a notch. At the
same time, the growth in functional programming (LINQ in C#, F#,
Clojure, Scala) and separately, dynamic typing (Javascript, Python), and
fluent APIs/DSLs all give us better ways of both thinking about and
writing our code--we can be more expressive and simpler while being less
error-prone. C# is becoming the best of all worlds as these paradigms
become nearly fully enabled in C# with the variety of NuGet libraries
enabled.<br /></p><p>Here are some interesting NuGet packages that <em>I haven't used yet,</em> but might take advantage of soon.</p><ul><li><strong><a href="https://fluentvalidation.net/">FluentValidation </a>- </strong>On
my current work project, we just wrote a bunch of complicated (though
not complex) validation code, all custom, and mostly with nested if
statements. I wonder if this would be more readable and intuitive using
a library like this?<br /></li><li><span><span><a href="https://www.nuget.org/packages/Humanizer/"><strong></strong></a><strong><a href="https://github.com/Humanizr/Humanizer#humanize-string">Humanizer</a></strong></span></span>
- So much of our code is just variations on mapping identifiers,
timestamps, or logic into human-readable forms. This package provides a
ton of methods for doing this in a simpler and more intuitive way.<br /></li><li><a href="https://fluentmigrator.github.io/"><strong>FluentMigrator</strong> </a>-
So far, I've never worked on a project that has successfully kept its
database schema in code, other than by retaining SQL scripts. Could
this library help?<br /></li><li><a href="https://github.com/App-vNext/Polly#readme"><strong>Polly</strong> </a>-
Retries, timeouts, and more - I've often written custom methods to deal
with various service accessing needs. Next time I feel like I should,
I'll have a look at Polly.<br /></li><li><a href="https://github.com/mcintyre321/OneOf/#readme"><strong>OneOf</strong> </a>-
I see often where I need to return two different types--one for success
and one for an expected failure or "nothing to do" case. Functional
languages have handled this better than OO languages from the start, but
maybe this library could bring me the ability to return <span class="ms-rteFontFace-10 ms-rteThemeBackColor-1-1">OneOf<bool, FailureMessage></span> or any other union that might be useful.<br /></li><li><a href="https://dynamic-linq.net/"><span><strong>Dynamic LIN</strong></span><strong>Q</strong></a>
- Could it help or hurt to be able to dynamically build your LINQ logic
depending on runtime state? I've often wanted to, but understood why
C# can't do it by default. Could this package enable such power? Or
would it do more harm than good?</li></ul><div><br /></div><div>These are of course in addition to the ones I've already used repeatedly--if you don't know them well, take a look for sure:</div><div><br /></div><div><ul><li><strong><a href="https://github.com/StackExchange/Dapper#readme">Dapper</a> - </strong>EF
is great but I still think it is overfkill for most needs, especially
if your team is proficient in writing SQL (and they should be). I
cannot bear the thought of spending an hour to make EF do what I want
when I could just write my own SQL and be done with it. Dapper does the
part I want (Mapping rows to objects) and leaves out the part I don't
(Mapping code to queries).<br /></li><li><a href="https://fluentassertions.com/introduction"><strong>FluentAssertions</strong> </a>- It is so much better
to write intuitive assertions in my tests than to even take the 20
seconds to write it with traditional MSTest assertion syntax. Goodbye
Assert.IsTrue, hello result.Should.<br /></li><li><strong><a href="https://github.com/moq/moq4#readme">Moq </a>- </strong>There
seems to be nothing better for detouring/mocking in your automated
tests. Even though .Setup and It.IsAny<T> get tiresome, I haven't
found anything better.</li><li><strong><a href="https://github.com/domaindrivendev/Swashbuckle.WebApi#readme">Swashbuckle</a> </strong>with <span><span>Swagger and SwaggerUI </span></span>- This trio gives my API documentation with hardly any effort. Nice!</li><li><strong><a href="https://serilog.net/">Serilog </a>- </strong>We
finally have a winner in the logging library fight. Everyone loves it;
you will too. Ditch Log4net, nLogger, or anything else you might have
struggled with before.</li></ul></div></div></div></div>Patrickhttp://www.blogger.com/profile/10070636843509784651noreply@blogger.com0tag:blogger.com,1999:blog-2796910034566353989.post-12388810500984011392020-04-12T18:12:00.000-05:002020-05-04T17:55:24.510-05:00See me at the all-online Minnesota Developer's Conference, May 4, 2020On May 4, 2020, I gave a talk on <a href="https://mndevconf.com/speakers/patrick-szalapski">Practical Refactoring in C#</a> at the <a href="https://mdc.ilmservice.com/">Minnesota Developer's Conference, live online</a>. The all-day virtual conference was only $20 and has some of the best speakers around on C#, Blazor, Javascript, Azure, and soft skills for developers.<br />
<br />
<a href="https://mdc.ilmservice.com/sessions#2598">My session is all about how to make your C# code better</a> and to do it more routinely and with high-confidence. Too often, we are afraid to refactor because we might break something--but we err on the side of false caution, because our dirty, confusing code causes more bugs than a refactoring would. I'll show my favorite refactorings and demonstrate how to get into the habit of always improving.<br />
<br />
I also am excited to see my colleagues <a href="https://mdc.ilmservice.com/speakers/kamran-ayub">Kamran Ayub</a>, <a href="https://mdc.ilmservice.com/speakers/erik-onarheim">Erik Onarheim</a>, and <a href="https://mdc.ilmservice.com/speakers/elsa-vezino">Elsa Vezino</a> present there, and I always love more <a href="https://mdc.ilmservice.com/speakers/mike-benkovich">Benko-tips</a> and <a href="https://mdc.ilmservice.com/speakers/david-ward">wisdom from Ward</a>. Thanks a ton to Jason Erdahl and his team at ILM for moving this thing online rather than cancelling--I've never been as interested in a live online conference before!<br />
<br />Video: <a href="https://www.youtube.com/watch?v=RCdtf1OzLyQ">Practical Refactoring in C#</a> <br />
<br />
<a href="https://www.slideshare.net/PatrickSzalapskiCSP/practical-refactoring-in-c"><b>Slides</b></a><br />
<br />
<b>Demo Code</b><br />
<ul>
<li><a href="https://github.com/szalapski/AsyncAwaitPain/blob/master/AsyncAwaitPain.WebApi/Controllers/AccountController.cs">AccountController</a></li>
<li><a href="https://github.com/szalapski/AsyncAwaitPain/tree/run-base/psz/Lib">GreetingProvider</a></li>
</ul>
<b>My favorite refactorings:</b><br />
<ul>
<li>Tool-assisted:<br /><ul>
<li>Rename</li>
<li>Extract method</li>
<li>Move</li>
<li>Remove unused usings</li>
<li>Reduce nesting: Invert if; combine singly-nested Ifs; </li>
<li>Extract interface</li>
<li>Restrict access modifiers</li>
<li>Code against nonexisting members then generate </li>
</ul>
</li>
<li>Manual:<br /><ul>
<li>Simplify boolean logic</li>
<li>Early return</li>
<li>Reduce newline usage</li>
<li>Convert If-else to ternary</li>
<li>Eliminate/simplify loops with LINQ</li>
<li>Iterate instead of repeat code (try lambdas or private classes)</li>
<li>Replace Start/Stop methods with an IDisposable</li>
<li>7 steps to add dependency injection<b><br /></b></li>
</ul>
</li>
</ul>
<b>Other sources:</b><br />
<ul>
<ul>
<li>Sourcemaking on <a href="https://sourcemaking.com/refactoring/refactorings">Refactoring Techniques</a> </li>
<li><a href="https://www.jetbrains.com/help/resharper/Main_Set_of_Refactorings.html">Refactorings in Resharper</a> </li>
<li><a href="https://www.thekua.com/publications/RefactoringCheatSheet.pdf">Refactoring patterns cheat sheet</a></li>
</ul>
</ul>
Patrickhttp://www.blogger.com/profile/10070636843509784651noreply@blogger.com0tag:blogger.com,1999:blog-2796910034566353989.post-89934324645757588042020-02-02T14:48:00.002-06:002020-02-02T14:48:45.956-06:00Presenting at Twin Cities .NET User Group, Thu Feb 6 at 5:00On Thursday, February 6, I will be presenting at the Twin Cities .NET User Group on <a href="https://www.meetup.com/tcdnug/events/267808214/">Practical Refactoring With C#</a>. It is at 5:00 PM at General Mills in Golden Valley, MN. Please introduce yourself afterward if I haven't met you before. Hope to see you there.
Patrickhttp://www.blogger.com/profile/10070636843509784651noreply@blogger.com0tag:blogger.com,1999:blog-2796910034566353989.post-81550230013694289342019-05-13T12:52:00.001-05:002019-05-13T12:52:01.365-05:00Easy screen resolution changes<p><font size="3">I’m using the following <a href="https://www.autohotkey.com/">AutoHotKey shortcuts</a> to quickly change my screen resolution on my 27” 4K display from 4K (normal work) to 1080p (or “2K”, for screen sharing over Skype, MS Teams, or Slack). I simply press Win+Alt+2 for 2K and Win+Alt+4 for 4K. Enjoy!<br><br><font face="Consolas" size="2">
#!4::<br>
ChangeResolution(3840, 2160)<br>
Return</font></font></p><font face="Consolas" size="2">
</font><p><font face="Consolas" size="2">#!2::<br>
ChangeResolution(1920, 1080)<br>
Return</font></p><font face="Consolas" size="2">
</font><p><font face="Consolas" size="2">ChangeResolution(Screen_Width := 1920, Screen_Height := 1080, Color_Depth := 32)<br>
{<br> VarSetCapacity(Device_Mode,156,0)<br> NumPut(156,Device_Mode,36) <br> DllCall( "EnumDisplaySettingsA", UInt,0, UInt,-1, UInt,&Device_Mode )<br> NumPut(0x5c0000,Device_Mode,40) <br> NumPut(Color_Depth,Device_Mode,104)<br> NumPut(Screen_Width,Device_Mode,108)<br> NumPut(Screen_Height,Device_Mode,112)<br> Return DllCall( "ChangeDisplaySettingsA", UInt,&Device_Mode, UInt,0 )<br>
}<br>
Return</font></p>Patrickhttp://www.blogger.com/profile/10070636843509784651noreply@blogger.com0tag:blogger.com,1999:blog-2796910034566353989.post-84850082442836954362019-05-09T20:47:00.001-05:002019-05-10T10:59:20.152-05:00Billy Hollis on interaction design<p><img align="right" style="border: 0px currentcolor; border-image: none; float: right; display: inline; background-image: none;" alt="Image result for billy hollis" src="https://pluralsight.imgix.net/author/lg/billy-hollis-v2.jpg?w=200" border="0"><font size="3">I just finished attending </font><a href="https://ndcminnesota.com/"><font size="3">NDC Minnesota 2019</font></a><font size="3">. One of the highlights of the conference was </font><a href="https://twitter.com/billyhollis"><font size="3">Billy Hollis</font></a><font size="3">’s </font><a href="https://ndcminnesota.com/speaker/billy-hollis/"><font size="3">session on UX Principles, as well as his workshop</font></a><font size="3"> on practical steps to tackle a design task.</font></p><p><font size="3">Big takeaways:</font></p><ul><li><font size="3">Users have much Inattentional blindness--they shut out anything other than the things we want to see or expect to see. Don’t fight this.</font></li><li><font size="3">When trying to improve or iterate upon a app’s design, first make observations about the user’s experience and write them down in short, simple, English sentences. (These sentences do not go onto the product backlog yet—they are unactionable observations, not user stories.) Then sort by importance, filter, and dedupe them, and then discuss as a team, and only 6-10 design tasks (or stories) to go onto the product backlog.</font></li><li><font size="3">Know what is most important to the users. Most experiences suffer for lack of emphasis or visual cues of relative importance.</font></li><li><font size="3">Sketch/mockup designs. Use a low-fidelity tool or just pencil or whiteboard. </font></li><ul><li><font size="3">Sketch 3 or 4 designs, not just one. One sketch usually results in very particular, nitpicking feedback, but 3 or 4 sketches gives the opportunity for a range of reactions on the things that matter most</font></li><li><font size="3">Shoot for a “traditional” sketch, an “innovative” sketch, and a pushing-the-envelope sketch. Often this gets the stakeholders pushing themselves in the right direction. Don’t pigeonhole yourself to a </font></li></ul><li><font size="3">The point of good aesthetics is to give impression of high-quality software; always focus on actually having high-quality software without putting lipstick on a pig.</font></li></ul><p><font size="3">Hollis is one of my favorite presenters; if you get a chance to see him, I highly recommend it. He has a Pluralsight course on </font><a href="https://www.pluralsight.com/courses/cux-designprinciples"><font size="3">UX principles</font></a><font size="3"> and speaks around the country. </font></p>Patrickhttp://www.blogger.com/profile/10070636843509784651noreply@blogger.com0tag:blogger.com,1999:blog-2796910034566353989.post-55758881291948182502017-11-18T13:07:00.001-06:002017-11-18T13:18:54.777-06:00Tweaking Scrum<p dir="ltr">I presented a short talk called <a href="https://www.slideshare.net/secret/M66bvQ8EzQvVvf"><i>Tweaking Scrum</i></a> at Agile Day Twin Cities 2017. I hope to write a longer post on it, but for now, here is the slide deck, as well as <a href="https://i.imgur.com/smEQtgH.jpg">notes from the followup</a> open space discussion.</p>
Patrickhttp://www.blogger.com/profile/10070636843509784651noreply@blogger.com2tag:blogger.com,1999:blog-2796910034566353989.post-48011669177869342172017-05-11T19:23:00.001-05:002017-05-15T08:22:08.489-05:00Microsoft Build 2017 conference Liveblogged notes<p>Let me know if you want further thoughts on anything below. What a busy conference! It is more crowded than I thought, and I definitely not bored. </p> <p>TLDR: Go to <a href="https://channel9.msdn.com/Events/Build/2017" target="_blank">Build 2017 on MSDN Channel 9</a> to search for the videos that interest you the most. The best education for me was <a href="https://channel9.msdn.com/Events/Build/2017/B8001" target="_blank">the New in .NET “three runtimes” session by Hanselman and Hunter</a>, but the most impressive was certainly <a href="https://channel9.msdn.com/Events/Build/2017/B8083" target="_blank">Saqib Shaikh’s blind coding demo in the first five minutes of the “what’s new in Visual Studio” session</a>.</p> <p>Full notes:</p> <ul> <li> <p><a href="https://channel9.msdn.com/Events/Build/2017/KEY01" target="_blank">Keynote 1 – Satya Nadella, Scott Guthrie</a></p> <ul> <li>IoT/Cloud and “Intelligent Edge”: now able to sync logic in Azure cloud to devices apart from the cloud <li>Hanselman: Azure CLI, .NET Standard, PostgreSQL and MySql on Azure; SnapPoints (see breakout sessions) <li>AI / learning APIs: language, text, audio, more</li></ul> <li><strong><a href="https://channel9.msdn.com/Events/Build/2017/B8001" target="_blank">New in .NET</a>:</strong> Scott Hanselman & Scott Hunter <ul> <li>.NET Core is highly wanted; C# is #3 language, growing, talked about <li><strong>Architecture Guidance</strong> now available; see <a href="http://dot.net/architecture">http://dot.net/architecture</a>: samples, guides, etc. <li><strong>.NET Standard</strong> - formerly PCLs; a contract - allows sharing code, binaries, <ul> <li>No change to the way code works <li>For libraries, not for UI apps <li>UI might be Framework 4.7, Core, UWP, Xamarin, Unity, etc.</li></ul> <li><strong>.NET Core 2.0</strong> has one package reference for ASP.NET All <li><strong>VS 2017 Update 3:</strong> more refactoring features, live unit testing, C#7.1, <ul> <li>File globbing: drop files into folder; appears in solution explorer <li>Can edit .csproj without unloading <li>Live unit testing of .NET Core and Framework too. <li>Debugging: tells you WHY exceptions are thrown <li>Ctrl+T, "F" for file, "M" for member, etc. – reduces need for Resharper <li>Suggesting variable names, add a property with Ctrl+. – reduces need for Resharper <li>VS2017 Code style conventions and enforcement – reduces need for Resharper <li>Preview of FxCop live suggestions; live fix for you</li></ul> <li>Can build .NET Core on Mac using new Visual Studio for Mac <li>Azure <ul> <li>now has dependency map with warnings, key stats, errors; example errors are given with full detail <li>automatically takes snapshots (dumps) of state with errors from items running on Azure <li>Can set additional snappoints (not breakpoints) to get these dumps: doesn’t stop anything or slow things down; can do it in prodution; can look at with “<strong>snapshot debugger</strong>” – need to start collection and get snapshot as soon as it happens, but app doesn’t pause <li>Azure Functions: serverless functionality; based on .NET libraries; can debug locally or remote debug <ul> <li>can run functions based on triggers</li></ul></li></ul></li></ul> <li><strong><a href="https://channel9.msdn.com/Events/Build/2017/B8067" target="_blank">Containers with Kubernetes and Docker</a></strong>: Brendan Burns & Gabe Monroy </li> <ul> <li>Most outages are self-inflicted: our systems are complicated. We have understanding of our local space, but not about how our changes affect something else. <li>Usually we have Application, Server Cluster, Kernel/OS, and Hardware to worry a bout <ul> <li>“DevOps” is mostly about the Application layer, but it also should be about the rest of layers <li>Container Cluster Managers, Containers, IaaS/vitrualization decouple us from other layers</li></ul> <li>New system for Kubernetes orchestration: “<strong>Draft</strong>” making <strong>containers easier </strong>for developers<strong>; </strong>reduce conceptual overhead; demo: <ul> <li>"draft create” – scaffolds a docker app <li>“draft up” – sends app to ACS Docker Container cluster, automatically <li>Do not have to install Docker or Kubernetes on client, just need a remote ACS Docker Container cluster running (E.g. as set up from web hosting team)</li></ul> <li>New tool <strong>Helm </strong>– a package manager for your Kubernetes cluster <ul> <li>Helm needs to be installed as an app on Kubernetes cluster, then you can go nuts with packages without hassle <li>“helm search <term>” to look for packages <li>“helm install <packagename>” – installed, but also helps you configure it</li></ul> <li>Still need application management for multiple devs, source control, CI tools: example from <a href="https://about.gitlab.com/" target="_blank">GitLab</a> <ul> <li>set up a file .gitlab-ci.yml <li>uses a pipeline as set up in GitLab <ul> <li>spin up container, run build, deploy, spin down container <li>so close to Kubernetes that we can customize the actions in the pipeline</li></ul> <li>That’s great, but what if we aren’t using Gitlab?</li></ul> <li>These systems are becoming modular, more accessible to all. </li> <ul> <li>We don’t make GUIs anymore, we rely on existing OS and tools to do so <li>E.g. Kube Lego simiplifies LetsEncrypt to automate certificate acquisition whenever needed in a Kubernetes cluster – never need to manage certs</li></ul></ul> <li><strong><a href="https://channel9.msdn.com/Events/Build/2017/T6011" target="_blank">Azure CLI</a> - </strong>Kamaljit Bath:</li> <ul> <li>To manage Azure, you can use GUI, PowerShell, or new Azure CLI 2.0 (for general use) or 1.0 (for ASM services)</li> <li>Natural and idiomatic integration with bash and GNU/Linux tools</li> <li>Meant to fully support Azure services</li> <li>Can be installed on any OS with a native installer, or use the web version</li> <li>Examples:</li> <ul> <li>“az” list all commands</li> <li>“az account list” list subscriptions on my account</li> <ul> <li>“az account list –o tsv” for tab-separated values</li></ul> <li>“az vm list” list VMS on my subscription</li> <ul> <li>“az vm list –o json > file.json” get JSON version in a file</li></ul> <li>“az vm show –h” help with all the parameters for az vm show</li> <ul> <li>“az vm show –g <TAB>” for autocomplete on groups</li> <li>“az vm show –g MyresourceGroup –n Mynewdemovm –show-details”</li></ul> <li>“az find –q plan” – help on commands with "plan”</li> <li>“az vm list –o json | jpterm” – show JSON on left side and query results of a JMESPath expression on right side in jpterm</li> <ul> <li>“az vm list –query ‘[]{JmesExpression}’ – same thing without the tool</li></ul></ul> <li>Interactive mode – “az interactive”</li> <ul> <li>Gives much intellisense-like help, more examples to choose, help text</li> <li>“vm create ::1” runs first example for vm create</li> <li>Can go into specialized scopes, e.g. “cosmosdb”</li> <ul> <li>create –name MyCosmosName"….</li></ul></ul> <li>Search for “Get started with Azure CLI 2.0” on microsoft.com</li></ul> <li><strong><a href="https://channel9.msdn.com/Events/Build/2017/T6006-R2" target="_blank">Angular and Typescript</a> –</strong> Rob Wormald (Google)</li> <ul> <li>To be clear: “AngularJS” is Angular 1; Angular 2+ drops the “JS”</li> <li>Angular apps are made of components, and put together with decorators </li> <li>Everything Google does runs on Angular</li> <li>TypeScript and Open Source CLI tools have not been used inside Google – instead were using Google Closure Javascript that no one likes – now transitioning to TypeScript because:</li> <ul> <li>has inline types</li> <li>fast to recompile</li> <li>decorators</li> <li>is Google Closure-compatible (side-by-side)</li> <li>IDE support </li> <li>Use bazel.build instead of Grunt/gulp/webpack</li></ul></ul> <li><a href="https://blogs.msdn.microsoft.com/webdev/2017/02/20/lets-try-wcf-self-hosted-services-in-a-container/" target="_blank"><strong>WCF microservices in containers</strong></a><strong> </strong>is a work in progress<strong>– </strong>Jeff Fritz</li> <ul> <li>WCF is big and heavy, lots of power</li> <li>Must use Windows containers for WCF. Linux containers won’t work.</li> <li>Windows containers are big but easy to use. Good to get rid of managing VMs. We’ll have better SoAs.</li> <li>We support HTTP(S) and TCP bindings, IIS-hosting and self-hosting, anonymous endpoints “very easy” in a container</li> <ul> <li>Do not support MSMQ or HTTP authentication</li> <li>Can’t pass in environment variables on the command line, can’t orchestrate very easily – looking into extending Configuration Manager</li></ul> <li><a href="https://hub.docker.com/r/microsoft/wcf/" target="_blank">WCF dockerfile example</a></li> <li><a href="https://github.com/Microsoft/wcf-docker-samples" target="_blank">Sample code</a></li> <li>Visual Studio with standard ASP.NET project doesn’t understand WCF on Docker, nor debugging it</li> <li>Maybe better to use a self-hosted WCF host (“BasicHttpHost” in <a href="https://github.com/Microsoft/wcf-docker-samples" target="_blank">the sample code</a>) – still can’t debug</li> <li>How to do it: Install Docker for Windows, pull the latest microsoft/wcf image, try lift-and-shift an existing service,</li></ul> <li><a href="https://channel9.msdn.com/Events/Build/2017/KEY02" target="_blank">Keynote 2: Windows</a> – Terry Myerson, Joe Belfiore, Abolade Gbadegesin, Alex Kipman</li> <ul> <li>Windows 10 </li> <ul> <li>Fall Creators Update: new app Windows Story Remix (photos, videos, Paint 3D, work with phones): automatic videos and compositions like Google Photos; automatic sync with phones, ink notes that can be anchored to things in videos; Devs will have access to 3D/mixed reality features via APIs</li> <li>Fluent Design System will start influencing apps with light, depth, motion, material</li> <li>OneDrive Files On-Demand: mix of file on hard drive, cloud, or both in Windows Explorer; more automatic synching</li> <li>Windows Timeline: keeps track of all tasks from earlier; search; automatic syncs tasks across devices</li> <li>Connect your Android/iPhone to your Windows Phone</li> <li>Cloud-powered clipboard – copy/paste across devices (e.g. integrated into SwiftKey)</li> <li>XAML Standard 1.0 coming: will unify UWP + Xamarin UI code</li> <li>UWP features: pen integration, material, fluid transitions</li> <li>Graph APIs for people, activities, devices – built in to Windows 10, available via “Project Rome SDK” for Android and iOS</li></ul> <li>Windows Store + UWP apps</li> <ul> <li>Visual Studio Test Cloud will have full support for UWP and many devices</li> <li>Windows 10 S is a version of windows that is only for Windows Store Apps (for schools, other specialty apps)</li> <li>Apps like Spotify, iTunes, SAP Digital Boardroom, Autodesk Sketchbook or will be in Windows store</li></ul> <li>Dev Tools</li> <ul> <li>Ubuntu, SuSE Linux, Fedora soon be apps in Windows Store</li> <li>Xamarin Live Player: .NET C# Xamarin apps deployed to iOS device with full debugging capabilities, instant sync on UI changes</li> <li>Narration on every Windows 10 PC – can tell what the visually impared will experience; Narrator developer mode to put self in the shoes of a visually-impared person</li></ul> <li>Windows Mixed Reality: VR, AR; PCs, Consoles, and holographic computers/headsets</li> <ul> <li>Motions controllers: optical sensors, trigger, stick, touchpad – no external cameras</li> <li>headset + controllers from Acer for $400 coming 2017Q4 – can preorder at <a href="http://windows.com/mixedreality">http://windows.com/mixedreality</a></li></ul></ul> <li>Private conversation: <strong>F# </strong>could be good for creating DSLs or anything where composing/manipulating functions would be complicated in C#. Using duck typing-like type inference simplifies coding. Don’t be afraid to have most of an app in C# but a key part in F# if necessary.</li> <li><a href="https://channel9.msdn.com/Events/Build/2017/T6967" target="_blank">Developer’s Guide to the Galaxy</a> – snippets of various demos or talks</li> <ul> <li>Mixed reality is hard to develop? Who does all the 3D? It is possible to dev with only a PC and webcam. Using Unity, add a camera and a image to the scene. Add a 3D model from a .fbx (e.g. from Remix via Paint 3D), add to hierarchy, scale and position it.</li> <li>Chris Barker on MyPeople in Windows 10 Fall Creator’s Update: API is simple; can use toaster popup with XML</li> <li>David from France on <a href="http://webvr.info/samples" target="_blank">WebVR</a> and BabylonJS: full 3D and VR in the browser</li> <ul> <li>Render 3D scenes </li> <li>switch between plain view and stereoscopic fisheye view e.g. for Cardboard</li> <li>Can use C# and Typescript;</li> <li>Detect gaze, select objects, even gather video texture from webcam</li></ul> <li>JD - Progressive web apps: </li> <ul> <li>A “<strong>Service worker</strong>” runs independent of a browser frame – can speed up if the feature is available, enables offline functionality, not distruptive otherwise</li> <li>Use <a href="file:///C:/Users/G520844/AppData/Local/Temp/OpenLiveWriter1317746908/0823C88FB306/pwabuilder.com" target="_blank">PWA Builder</a> to help get started – gets a service worker ready for you to drop into your app</li> <li>Can also use hardware from Javascript web app</li></ul> <li>UWP Community Toolkit Sample App in the Windows Store – can copy XAML code, etc, from it</li></ul> <li><strong><a href="https://channel9.msdn.com/Events/Build/2017/B8902" target="_blank">.NET Q&A Panel</a> – </strong>Damian Edwards, Mike Harsh, Immo Landwerth, Rich Lander</li> <ul> <li>Portability Analyzer tells us what changes apps need for .NET Core. Tips:</li> <ul> <li>Use .NET Core 2.0. .NET Core 1.0 has much lower compatibility.</li> <li>Error messages have good suggestions (click the triangle)</li> <li>Compiler messages might be easier to start</li> <li>Be sure to use VS2017 Update 3</li></ul> <li>.NET Core still supports P/Invoke, even on Mac & Linux – COM interop is not supported, MSMQ not yet supported but would be Windows-only if it is supported; System.Drawing and Directory Services not supported</li> <li>Must call ConfigureAwait(false) all the time on the server? See also <a href="http://stackoverflow.com/questions/13489065/best-practice-to-call-configureawait-for-all-server-side-code" target="_blank">this Q</a>. The default of “true” is still better.</li> <li>Need Core 2.0 or Framework 4.6 for snappoints/debug snapshots</li> <li>Why use .NET Core rather than Framework for web apps: </li> <ul> <li>Cross-platform (Linux, Mac OS, ARM)</li> <li>Self-contained .NET libraries (don’t need to install new framework on server)</li> <li>.NET Core is changing quicker and getting more new features (it is also open-source)</li> <li>Some performance advantages</li></ul> <li>Other Framework vs. Core ideas</li> <ul> <li>.NET Framework is not going away, support will continue, but it won’t get too many new features</li> <li>Can run .NET Framework apps in containers – don’t rewrite things</li> <li>Can use a ASP.NET Core API in front of a Framework library</li> <li>Not recommended to run ASP.NET Core on .NET Framework—it is really meant for .NET Core</li></ul> <li>TLS 1.2 support in Framework 3.5 is in a hotfix, and must be enabled via registry key. It is fully in Framework 4.0</li> <li>Web Assembly support in .NET? Nothing decided yet (probably not)</li> <li>How to load different versions of the same assembly? e.g. may get TypeEquivalenceExceptions – very hard to overcome with an easy way. Still using the later version is the easiest way.</li> <li>There is one specific use case that `async void` is for (event handlers); never use it otherwise. Probably need a way to catch it in Code Analysis.</li> <li>Can run Kestrel on Raspberry Pi? Not with UWP on Windows IoT. Can run a C++ Console app on Windows IoT. Not easy.</li> <li>Managed Add-in Framework not on .NET Core. No one cares about it.</li></ul> <li><a href="http://tinyurl.com/cnnaudio" target="_blank"><strong>Deep Learning </strong>demo</a>:</li> <ul> <li>Started with audio data for leaks in a pipe; using a Jupyter notebook to walk through: run microphone ball through pipe, get WAV file, get integer values from it, Fourier transforms to get an array of arrays, then make images, then colorize them, then build a neural net, then train the model, then run model against any image to predict probability of an image showing a leak; show results in a 2x2 confusion matrix; then figure out ways to improve the model</li> <li>Real-world: trained with 2000 images was good enough</li> <li>Transfer Learning can help you train the model </li></ul> <li><strong><a href="https://channel9.msdn.com/Events/Build/2017/B8019" target="_blank">Visual Studio 2017 and the CI pipeline</a></strong>: Ahmed Metwally & Mark Wilson-Thomas</li> <ul> <li>Can configure CI in Azure and VSTS right from Visual Studio; build failure would happen right in Visual Studio status var - Not useful with Jenkins</li> <li><strong>Visual Studio now has tighter feedback</strong>: see problems as soon as they are made when coding</li> <ul> <li>When “in the zone”, don’t always want to fix all the little issues. VS can help us understand what Code quality is at..</li> <li>Change pending indicator is in the status bar</li> <li>New “Build & Code Analysis results” in the Changes pane helps you commit with confidence (or fix issues before you do)</li></ul> <li>Continuous Delivery extension for Visual Studio is new. Road map:</li> <ul> <li>Today: Support for ASP.NET, CI Build failure notification, link to build results, </li> <li>Next: in-IDE pull requests, code analysis in pull request</li></ul> <li>join on slack <a href="http://aka.ms/cd4vs_slack" target="_blank">aka.ms/cd4vs_slack</a></li></ul> <li><strong><a href="https://channel9.msdn.com/Events/Build/2017/B8021" target="_blank">Visual Studio 2017 Coding at 88 MPH Tips and Tricks</a></strong><strong>: </strong>Justin Clareburt and Alison Buchholz<br>all at <a href="http://aka.ms/vsnavshortcuts" target="_blank">aka.ms/vsnavshortcuts</a>, below are the ones that were new to me:</li> <ul> <li>Git Repos from other machines show up on Start Page – easy cloning</li> <li>Folder view supported in solution explorer – do not need projects</li> <li>Shift+Alt+Enter: full screen mode</li> <li>Esc: go to main text editor – Alt+F6 go to recent tool windows</li> <li>Shift+Esc: close current tool window</li> <li>Suggested mappings:</li> <ul> <li>Window.Dock: Ctrl+Insert</li> <li>Wondow.DockAsTabbed: Ctrl+Shift+insert</li> <li>Window.AutoHide: Shift+Insert</li> <li>Windows.HideAll: Shift+Alt+Ins</li></ul> <li>Move to next/prev tab Ctrl+Alt+PgDn and +PgUp</li> <li>Ctrl+T: Go to all box with filters</li> <li>Shift+F12: find all references</li> <li>Ctrl+Alt+Home: keep previewed doc</li> <li>Alt+W, L: Close all documents</li> <li>Ctrl+Shift+F12: go to first error</li> <li>Solution Explorer: Sync with active document button (or track active document option on )</li> <li>Extensions</li> <ul> <li>Git Diff Margin: </li> <li>Solution Error Visualizer</li> <li>Fix Mixed Tabs </li> <li>Hot Commands</li> <li>Open Output Folder</li> <li>Add Folder to Solution</li> <li>Multi Edit Mode</li></ul></ul> <li><strong><a href="https://channel9.msdn.com/Events/Build/2017/B8083" target="_blank">The future of Visual Studio</a> – </strong>Amanda Silver, Tim Sneath</li> <ul> <li>Blind coder demo – Saqib Shaikh. Pretty amazing how a blind person can code efficiently.</li> <li>Visual Studio is the most popular IDE for web developers; C# is the third-most popular language</li> <li>VS2017 version 15.3 will be out soon; lots of new features</li> <ul> <li>Emphasis: Catching issues early (“shift left”)</li> <ul> <li>Realtime experiences for unit tests, violations</li></ul> <li>New installer, choose only the parts you need</li> <li>Live unit testing </li> <li>Refactorings: sorts, null checks, string interpolation, many more</li> <li>Code Analysis extension</li> <li>“Enable Docker Support” in new Project dialog</li> <li>True Linux C++ development in Visual Studio</li> <li>Visual Studio Data Scientist mode for R</li></ul> <li>Visual Studio for Mac</li> <li>Experiment: VS can infer EditorConfigFile for style rules</li> <li>Linux subsystem – full Linux apps</li></ul> <li><strong><a href="https://channel9.msdn.com/Events/Build/2017/B8104" target="_blank">The future of C#</a> – </strong>Dustin Campbell, Mads Torgensen</li> <ul> <li>C# 7 and 7.1</li> <ul> <li>tuples, tuples with named items</li> <ul> <li>Infer names of Tuple items in C# 7.1</li></ul> <li>async Task Main now allowed</li> <li>new Task types – ValueTask (good for memory intensive tasks</li> <li>Deconstruction: get values underneath a tuple</li> <ul> <li>underscore for items we don’t care about</li></ul> <li>Local functions</li> <li>Pattern matching (types or constants), even in switch, even with conditions</li> <li>Inline variable declaration `out int i`</li></ul> <li>VS features</li> <ul> <li>New braces connected with faint lines</li> <li>depend on C# version autofix in C# 7.1</li> <li>“convert to binary” refactoring</li> <li>Better “go to references” and “find references” </li> <li>Find references of literals</li> <li>Auto complete for partially typed namespaces/types</li> <li>Suggested parameter names (intellisense)</li> <li>Autofix for git-style merge complete</li> <li>Press enter to split a long string into concatenated strings</li> <li>Refactor to initialize property (like Resharper), separate nibbles</li></ul> <li>C# 8</li> <ul> <li>Default implementations inside interface (avoids requiring all implementations to implement new members)</li> <li>Annotated possibly null (e.g. `string? s` as a return value)</li> <li>foreach await, using await</li> <li>more flexible extension methods</li> <li>Records (automatically generated classes)</li></ul></ul> <li><strong><a href="https://channel9.msdn.com/Events/Build/2017/B8036" target="_blank">Supercharging debugging in Visual Studio</a></strong> – Kaycee Anderson</li> <ul> <li>Execution Control</li> <ul> <li>Remember to add command-line args in Project Properties – Debug</li> <li>Can start debugging with F10 – first point</li> <li>Recent function returned values now show in locals; to see in watch window with “$returnValue1”</li> <li>New choice “step into specific” instead of stepping to the first one</li> <li>New icon for “run to click” just like run to cursor, but more discoverable</li> <li>Can “run to cursor” from call stack window, instead of step out, step out, step out</li> <li>`if (debugger.IsAttached) Debugger.Break();` – hardcoded breakpoint</li></ul> <li>Data inspection</li> <ul> <li>Choose among several visualizers in data tips</li> <li>Alt+F12 for peek window</li> <li>Hold Ctrl to hide intellisense overlays</li> <li>Can right-click to add expressions to pinned watch snips</li> <li>“Make Object ID” makes a reference $1 that you can use in watch window even when other references change</li></ul> <li>Breakpoints & Diagnostics</li> <ul> <li>Breakpoint actions: make conditional, make tracepoint</li> <li>quick and dirty profiling – see pertips at the right side of each recently-executed line</li> <li>consider keeping Diagnostics tools window – now has more useful summaries</li> <ul> <li>take snapshots to compare two points in time e.g. memory</li></ul> <li>Coming soon: historical debugging via “Step back”</li></ul> <li>Exceptions: New exception helper in debugging – has…</li> <ul> <li>inner exception scroller</li> <li>more information on cause of exception, </li> <li>optional exception settings (also there’s an exception settings window)</li></ul> <li>Multithreading</li> <ul> <li>Parallel stacks window: shows visualization of all threads</li> <li>Parallel watch window: see values across all threads</li> <ul> <li>Can freeze thread in parallel watch window, run all other threads</li></ul> <li>After a breakpoint is hit, remove breakpoint to avoid hitting the same breakpoint in other threads (step around)</li> <ul> <li>can filter breakpoint to a thread ID</li></ul></ul> <li><a href="http://aka.ms/vsdiagsamples" target="_blank">aka.ms/vsdiagsamples</a> – the samples for the above</li> <li>aka.ms/diagnostics </li> <li>aka.ms/SuperChargeYourDebugging</li></ul> <li>Miscellaneous notes on the side </li> <ul> <li>.NET Conf 2017 is Sep 19-21: free online conference <li><a href="http://dotnetfringe.org/" target="_blank">.NET Fringe</a> conference June 4-6</li> <li>Recurring theme: increasing reliance on CLIs, especially ones that look like bash</li></ul></ul> <ul> <ul></ul></ul>Patrickhttp://www.blogger.com/profile/10070636843509784651noreply@blogger.com0tag:blogger.com,1999:blog-2796910034566353989.post-32808496999865085712017-05-07T10:05:00.001-05:002017-11-18T13:12:55.939-06:00Presenting at DevJam's ProductConf 2017 on Tweaking ScrumI know it's last minute, but I will be leading a talk on Tweaking Scrum for Better Products, this Monday May 8, 2017, at <a href="http://devjam.com/productconf_2017/">DevJam's ProductConf in St. Paul It looks like there is still time to register</a>!<br><br>
<span style="background-color: white; color: #222222; font-family: inherit;">It is a mistake to leave the product decisions to the Product Owner alone. Every member of an always-improving, self-organizing Scrum team will help to make the team's work and the product better. This highly collaborative session will have a section on prepared insights on how to tweak typical Scrum practice as learned from several years of software development on Scrum teams; then the bulk of the time will be open for your insights on how you have tweaked Scrum that you might share with the group. However, this discussion will not simply be show-and-tell time; instead, we will think through the suggestions from a product lens in order to see if the potential impact is outweighed by missing out on the benefits from "untweaked" Scrum. We will avoid dogmatic thinking but not controversy as we tweak Scrum together.<br><br></span>
If you are coming to ProductConf, be sure to come over and say hi!<div><br></div><div>Update: here is the slide deck for that talk: https://www.slideshare.net/secret/M66bvQ8EzQvVvf</div>Patrickhttp://www.blogger.com/profile/10070636843509784651noreply@blogger.com0tag:blogger.com,1999:blog-2796910034566353989.post-34168131095622370372017-03-28T11:13:00.004-05:002017-03-28T11:13:42.042-05:00I'll keep this short as it isn't software-related, but I started a new "amateur philosophy" podcast called "Objections to Objectivism", which explains, explores, and criticizes the philosophy of Ayn Rand. Check out the <a href="http://objectionstoobjectivism.blogspot.com/">Objections to Objectivism site</a>, the <a href="https://www.blogger.com/feeds.soundcloud.com/users/soundcloud:users:96365479/sounds.rss">O2O podcast feed</a>, or find it on any podcast app like iTunes, Stitcher, TuneIn, or Podcast Addict.Patrickhttp://www.blogger.com/profile/10070636843509784651noreply@blogger.com0tag:blogger.com,1999:blog-2796910034566353989.post-54762286968141085092016-11-01T08:36:00.001-05:002016-11-01T08:49:20.210-05:00Scrum Guide updated to include valuing commitment, focus, openness, respect, courage<p>Recently, Ken Schwaber and Jeff Sutherland updated the official Scrum Guide to include <a href="https://lh3.googleusercontent.com/-li-b3Qnn9z0/WBiboN3cH6I/AAAAAAAALj0/kL99OfJpLW4_LTSBeEFrrrPSL7ElNRk_wCHM/s1600-h/SchwaberSutherlandJuly2016%255B6%255D"><img title="SchwaberSutherlandJuly2016" style="border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; float: right; padding-top: 0px; padding-left: 0px; border-left: 0px; margin: 6px 0px; display: inline; padding-right: 0px" border="0" alt="SchwaberSutherlandJuly2016" src="https://lh3.googleusercontent.com/-PnNDhrBQecU/WBiboj9RWNI/AAAAAAAALj4/ReIfMik7gJkloLLC1e-mm4-A1eN4dCtqgCHM/SchwaberSutherlandJuly2016_thumb%255B4%255D?imgmax=800" width="285" align="right" height="152"></a>the Scrum values, which are: <ul> <li>Commitment</li> <li>Focus</li> <li>Openness</li> <li>Respect</li> <li>Courage</li></ul> <p>Sutherland emphasizes that teams adopting Scrum must have a <strong>commitment</strong> to change and to take your company into the future. Without embracing this change, "you can't get there" and Scrum will be of little value to you. <p>Schwaber highlighted that <strong>focus</strong> to work on nothing but the sprint goal will yield the best results; working on other items destroys your productivity. Sutherland noted that doing twice the work in half the time is only possible if the teams focus on the sprint goal alone. <p>Stakeholders and the team alike need to have a spirit of <strong>openness</strong>. Sutherland stressed that all happenings must be visible; only when everyone knows what is happening can you best decide how to adjust and how long work will take. We should go so far as not to work on anything that isn't visible. <p>These are only possible with <strong>respect</strong> for each other; blame and cover-ups result in poor outcomes. If we respect everyone where they are, they can be empowered to grow and achieve. Sutherland says this applies even to high-pressure cutthroat venture capital-driven startups. Schwaber stresses that respect creates a positive hubbub, a buzz that is charged up with work getting done. <p><strong>Courage</strong>, both to do the right thing and to attempt hard things, is necessary because change is risky. Leaders have a big role to encourage taking wise risks and accepting the downside that sometimes results. Schwaber emphasizes that Scrum provides a safety net--you will never waste more than a sprint's worth of work. <p>Scrum has the power to make your work life more fun, more balanced, and happier.</p> <p>View the Scrum Guide: <a href="http://www.scrumguides.org/scrum-guide.html">http://www.scrumguides.org/scrum-guide.html</a> - The newest changes are near the beginning, under "Scrum Values". Patrickhttp://www.blogger.com/profile/10070636843509784651noreply@blogger.com2tag:blogger.com,1999:blog-2796910034566353989.post-85421446065386386132016-02-12T18:40:00.025-06:002024-01-26T09:04:44.743-06:00A compact layout bookmarklet for Azure DevOps work items<p><b>UPDATE 2024-01: Now works for the task board better after 2023 UI changes from Microsoft.</b><br />
<br />
Do you want your UI controls and whitespace cruft taking up half your browser window, like the default view in the first image? Wouldn’t you like your Azure DevOps backlog to instead show as much info as possible, as on the second image below?</p><p>You can! Drag this to your bookmark bar (Firefox or Chrome): <b><a href="javascript:/* version 2024-01-26 */(function () { var s = document.createElement('style'); s.innerText = ` /* for backlog list */ #header-row {display: none;} .header-section .hubs-section {left:300px; top:-5px;} .nav-separated .hub-groups-section, .nav-separated .search-box {margin-top:0; padding-top:0;} .header-post11 .hub-groups-section li, .bolt-page .page-content-bottom {margin-bottom:0; padding-bottom:0;} .header-post11 #navWrapper {height:20px;} .header-post11 + .content-section {top:20px;} .header-post11 .hub-groups-section {padding-bottom: 0;} .hub-title {padding-top:9px; padding-bottom:9px;} .hub-content .right-hub-content .hub-pivot { margin-left:300px; margin-right:340px;} .hub-view.explorer .right-hub-content {top:3px;} .backlog-header .toolbar .menu-bar > .menu-item{margin-top:0; margin-bottom:0; padding-top:0; padding-bottom:0; } .backlog-header .toolbar .menu-bar > .menu-item.text-filter-box {margin-top: 0 !important;} .backlog-header .toolbar .menu-bar > .menu-item.toggle-filter-bar {padding-bottom:0; padding-top: 0 !important;} .backlog-header .toolbar .menu-bar > .menu-item.toggle {margin-top: 0 !important;} .backlog-header .toolbar { height:25px; margin-bottom:0;} .backlog-header { height:auto; margin-bottom:0;} .productbacklog-grid-results.sprint-backlog.grid {margin-top:0;} .grid-focus {height: 0;} .grid-header-column > .title {padding-top: 0; } .grid-row.closed-item-tfsWebCompact {color:green;} .grid-row.closed-item-tfsWebCompact .grid-cell:nth-child(3) {text-decoration:line-through;} /* for board */ .wit-card:focus{ background-color: #bfb; } .wit-card:focus:hover{ background-color: #cfc; } .wit-card:hover{ background-color: #efe; } .id-title-container, .board-content-details .task-list .add-task-container, .agile-board .board-tile .board-tile-content .child-tasks-summary, .board-content-details .task-list .task, .tags.field-container, .additional-field.lastAdditionalField, .board-tile .container, .taskboard-cell .id-title-container, .taskboard-cell .witExtra , .wit-card.taskboard-card .fields .field-container, .wit-card .editable-card-identity-picker .card-assigned-to, .wit-card.taskboard-card .work-item-state-container { margin:0 } .add-task-container, .vss-Hub .vss-HubPivotBar, .vss-PivotBar .vss-PivotBar--bar.tall .vss-PivotBar--commandBar, .page-content-top.page-content, .bolt-page > .bolt-header, .flex.flex-column.rhythm-horizontal-4.padding-top-16 {padding:0;} .repos-pr-header.bolt-header, .repos-pr-title input, .pr-secondary-title-row, .repos-compare-toolbar, .vss-Hub .vss-HubPivotBar:not(.shadowStyle), #taskboard-table-header th.taskboard-cell, .bolt-tabbar-tabs.tall .bolt-tab, .bolt-button { padding-block: 0; margin-block: 0; height: auto; } .repos-changes-viewer .sticky, .repos-compare-toolbar .margin-vertical-8, .repos-changes-viewer.is-folder > :first-child { padding-block : 2px; margin-block: 2px; } .wit-card .card-content{ padding: 8px; } .taskboard-card{ margin: 3px; } .navigation-container .flex-column {width: 32px;} .project-navigation .navigation-icon { width: 30px; } .vss-PivotBar .vss-PivotBar--pivots.tall .vss-PivotBar--button, .vss-PivotBar .vss-PivotBar--bar.tall .vss-PivotBar--commandBar { height: auto; } .wit-card .editable-field.is-identity{ min-height: 20px; margin-top: 2px; } .tbTile{width: 200px; margin: 5px;} .tbTile.parentTbTile{width: 140px;} .taskboard-parent{min-width:auto; width:140px; padding-right:0;} .taskboard-expander { width: 12px;} .taskboardTableHeaderScrollContainer .taskboard-parent{min-width:146px; width:140px; padding-right:0;} .taskboard-cell { padding: 2px;} .taskboard-cell .id-title-container{ padding-right:0; } .taskboard-cell .witExtra { margin-bottom: 3px;} .taskboard-cell:not(.taskboard-parent) .field-container.additional-field .field-label:first-child { display:none; } .sprint-view-container .vss-PivotBar .vss-PivotBarItem.customPadding { padding-inline: 5px;} .field-container.additional-field {padding-left: 4px;} /* make the "Closed" column really compact */ td[axis="taskboard-table-body_s2"] .tbTile {font-size:10.5px;} td[axis="taskboard-table-body_s2"] .tbTile .id {line-height:10px;} td[axis="taskboard-table-body_s2"] .tbTile .title {line-height:12px;} td[axis="taskboard-table-body_s2"] .tbTile .id-title-container {line-height: 10px;} td[axis="taskboard-table-body_s2"] .tbTile .tbTileContent .container.witExtra { max-height: 12px; } td[axis="taskboard-table-body_s2"] .witExtra {height:18px;} td[axis="taskboard-table-body_s2"] .witExtra .user-picture-resolved { display:none;} td[axis="taskboard-table-body_s2"] .witExtra .element-height-medium {line-height:unset;} td[axis="taskboard-table-body_s2"] .work-item-type-icon-host { display:none;} /* "make need analyst testing" column narrower */ table.sticky-table colgroup col:nth-child(5){ width: 12.5% !important; } /* row numbers on task board */ table.sticky-table{ counter-reset: number; } table.sticky-table td .taskboard-expand-collapse-button{ position:relative; counter-increment: number; } table.sticky-table td .taskboard-expand-collapse-button:after{ position: absolute; writing-mode: vertical-rl; /* fallback for browsers that don't support sideways-lr */ writing-mode: sideways-lr; white-space: nowrap; font-size: 120%; content:"ROW " counter(number); top: 30px; } table.sticky-table td.taskboard-collapsed-row .taskboard-expand-collapse-button:after{ /* hide on collapsed rows */ display: none; } `; document.head.appendChild(s) })();">AzDO Compact</a></b> for Azure DevOps work items/backlogs
</p><p></p><p><br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgKLtHwwj1N0eXjJzV8009GUs52i6LfcuE7qDFFYpHZ6tGuZfOva0ue-j_U85Ihvttyla9OzI5OjOlSBzL02iBTWgNQT4vz38v1Bdja-4ICh_2yIMz9LZQrVlaPNqnyw9QbNjzgWOmS8wHw/s1600/tfscompact1.png"><img border="0" height="598" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgKLtHwwj1N0eXjJzV8009GUs52i6LfcuE7qDFFYpHZ6tGuZfOva0ue-j_U85Ihvttyla9OzI5OjOlSBzL02iBTWgNQT4vz38v1Bdja-4ICh_2yIMz9LZQrVlaPNqnyw9QbNjzgWOmS8wHw/s640/tfscompact1.png" width="640" /></a><br />
<br />
You can! Drag this to your bookmark bar (Firefox or Chrome): <a href="javascript:/* updated 2024-01-18 */(function () { var s = document.createElement('style'); s.innerText = ` /* for backlog list */ #header-row {display: none;} .header-section .hubs-section {left:300px; top:-5px;} .nav-separated .hub-groups-section, .nav-separated .search-box {margin-top:0; padding-top:0;} .header-post11 .hub-groups-section li {margin-bottom:0; padding-bottom:0;} .header-post11 #navWrapper {height:20px;} .header-post11 + .content-section {top:20px;} .header-post11 .hub-groups-section {padding-bottom: 0;} .hub-title {padding-top:9px; padding-bottom:9px;} .hub-content .right-hub-content .hub-pivot { margin-left:300px; margin-right:340px;} .hub-view.explorer .right-hub-content {top:3px;} .backlog-header .toolbar .menu-bar > .menu-item{margin-top:0; margin-bottom:0; padding-top:0; padding-bottom:0; } .backlog-header .toolbar .menu-bar > .menu-item.text-filter-box {margin-top: 0 !important;} .backlog-header .toolbar .menu-bar > .menu-item.toggle-filter-bar {padding-bottom:0; padding-top: 0 !important;} .backlog-header .toolbar .menu-bar > .menu-item.toggle {margin-top: 0 !important;} .backlog-header .toolbar { height:25px; margin-bottom:0;} .backlog-header { height:auto; margin-bottom:0;} .productbacklog-grid-results.sprint-backlog.grid {margin-top:0;} .grid-focus {height: 0;} .grid-header-column > .title {padding-top: 0; } .grid-row.closed-item-tfsWebCompact {color:green;} .grid-row.closed-item-tfsWebCompact .grid-cell:nth-child(3) {text-decoration:line-through;} /* for board */ .wit-card:focus{ background-color: #bfb; } .wit-card:focus:hover{ background-color: #cfc; } .wit-card:hover{ background-color: #efe; } .id-title-container, .board-content-details .task-list .add-task-container, .agile-board .board-tile .board-tile-content .child-tasks-summary, .board-content-details .task-list .task, .tags.field-container, .additional-field.lastAdditionalField, .board-tile .container, .taskboard-cell .id-title-container, .taskboard-cell .witExtra , .wit-card.taskboard-card .fields .field-container, .wit-card .editable-card-identity-picker .card-assigned-to, .wit-card.taskboard-card .work-item-state-container { margin:0 } .add-task-container, .vss-Hub .vss-HubPivotBar, .vss-PivotBar .vss-PivotBar--bar.tall .vss-PivotBar--commandBar, .page-content-top.page-content, .bolt-page > .bolt-header, .flex.flex-column.rhythm-horizontal-4.padding-top-16 {padding:0;} .repos-pr-header.bolt-header, .repos-pr-title input, .pr-secondary-title-row, .repos-compare-toolbar, .vss-Hub .vss-HubPivotBar:not(.shadowStyle), #taskboard-table-header th.taskboard-cell, .bolt-tabbar-tabs.tall .bolt-tab, .bolt-button { padding-block: 0; margin-block: 0; height: auto; } .repos-changes-viewer .sticky, .repos-compare-toolbar .margin-vertical-8, .repos-changes-viewer.is-folder > :first-child { padding-block : 2px; margin-block: 2px; } .wit-card .card-content{ padding: 8px; } .taskboard-card{ margin: 3px; } .navigation-container .flex-column {width: 32px;} .project-navigation .navigation-icon { width: 30px; } .vss-PivotBar .vss-PivotBar--pivots.tall .vss-PivotBar--button, .vss-PivotBar .vss-PivotBar--bar.tall .vss-PivotBar--commandBar { height: auto; } .wit-card .editable-field.is-identity{ min-height: 20px; margin-top: 2px; } .tbTile{width: 200px; margin: 5px;} .tbTile.parentTbTile{width: 140px;} .taskboard-parent{min-width:auto; width:140px; padding-right:0;} .taskboard-expander { width: 12px;} .taskboardTableHeaderScrollContainer .taskboard-parent{min-width:146px; width:140px; padding-right:0;} .taskboard-cell { padding: 2px;} .taskboard-cell .id-title-container{ padding-right:0; } .taskboard-cell .witExtra { margin-bottom: 3px;} .taskboard-cell:not(.taskboard-parent) .field-container.additional-field .field-label:first-child { display:none; } .sprint-view-container .vss-PivotBar .vss-PivotBarItem.customPadding { padding-inline: 5px;} .field-container.additional-field {padding-left: 4px;} /* make the "Closed" column really compact */ td[axis="taskboard-table-body_s2"] .tbTile {font-size:10.5px;} td[axis="taskboard-table-body_s2"] .tbTile .id {line-height:10px;} td[axis="taskboard-table-body_s2"] .tbTile .title {line-height:12px;} td[axis="taskboard-table-body_s2"] .tbTile .id-title-container {line-height: 10px;} td[axis="taskboard-table-body_s2"] .tbTile .tbTileContent .container.witExtra { max-height: 12px; } td[axis="taskboard-table-body_s2"] .witExtra {height:18px;} td[axis="taskboard-table-body_s2"] .witExtra .user-picture-resolved { display:none;} td[axis="taskboard-table-body_s2"] .witExtra .element-height-medium {line-height:unset;} td[axis="taskboard-table-body_s2"] .work-item-type-icon-host { display:none;} /* "make need analyst testing" column narrower */ table.sticky-table colgroup col:nth-child(5){ width: 12.5% !important; } /* row numbers on task board */ table.sticky-table{ counter-reset: number; } table.sticky-table td .taskboard-expand-collapse-button{ position:relative; counter-increment: number; } table.sticky-table td .taskboard-expand-collapse-button:after{ position: absolute; writing-mode: vertical-rl; /* fallback for browsers that don't support sideways-lr */ writing-mode: sideways-lr; white-space: nowrap; font-size: 120%; content:"ROW " counter(number); top: 30px; } table.sticky-table td.taskboard-collapsed-row .taskboard-expand-collapse-button:after{ /* hide on collapsed rows */ display: none; } `; document.head.appendChild(s);})();">AxDO Compact</a> for Azure DevOps work items/backlogs <br />
<br />
Then, whenever you have TFS web work items or task board open and you want to see <b>more content and less cruft</b>, click it. Be sure to use "full screen view" in TFS and in your browser as desired.<br />
I’ve also made the 2015 version <a href="https://gist.github.com/szalapski/d2677564a0e89f735d31">available as a gist</a>.<br />
<br />
[<i>Bookmarklets</i> are tiny programs stored inside bookmarks or links. Similar to (but simpler than) add-ons and extensions, they add new tools to your web browser. Bookmarklets are shared on web pages as web links. To add a bookmarklet to your browser, right click on its link and choose the bookmark option, or drag it to your bookmark bar. To use it, simply click on the new bookmark you added.]</p>Patrickhttp://www.blogger.com/profile/10070636843509784651noreply@blogger.com0tag:blogger.com,1999:blog-2796910034566353989.post-17219840300947403372016-01-24T16:06:00.004-06:002016-09-19T10:34:39.917-05:00Basic Scrummaster Duties checklist<span style="font-size: medium;">I really like Michael James's <a href="https://www.scrumalliance.org/community/articles/2010/november/an-example-scrummaster-s-checklist" target="_blank">Scrum Master's Checklist</a> to give us an idea of the work a Scrum Master can do to make their team the best. However, it doesn't really show the basics of being a Scrum Master, so the following should help with that.</span><br />
<br />
<span style="font-size: large;">Scrum Master duties list:</span> <br />
<div>
Always: <br />
<ul>
<li>Nudge, steer, and help the team to do all Scrum events and activities </li>
<li>Help the product owner and team in any way to be more productive </li>
<li>Remember the Agile values </li>
</ul>
</div>
<div>
Before daily scrum:<br />
<ul>
<li>Ensure the team has updated the hours remaining on all tasks. </li>
<li>Update and post the burndown chart. </li>
</ul>
During daily scrum:<br />
<ul>
<li>Note all impediments reported by the team for later aggressive follow-up </li>
<li>Steer the team to stick to short answers to the three questions; steer them away from discussing problems or details during the scrum (Often say "this sounds like an important discussion; can we have it after the scrum?")</li>
</ul>
After daily scrum:<br />
<ul>
<li>Help team to overcome impediments as a top priority </li>
<li>Ensure relevant team members meet to overcome impediments <b>before</b> doing regular work </li>
<li>Keep outside distractions away from the team </li>
<li>Answer (or research) all Scrum questions the team has </li>
</ul>
Anytime, to prepare for sprint planning:<br />
<ul>
<li>Gather info on vacation/unavailable time for all team members in the next sprint </li>
<li>Ensure (help) the Product Owner refines and reorders the backlog </li>
<li>Ensure the product owner prepares a sprint goal </li>
<li>Schedule the next sprint planning </li>
<li>Schedule the sprint review; arrange for stakeholders to attend it. </li>
<li>If the stakeholders can't attend the Sprint Review, reschedule it. </li>
<li>Ensure the team will prepare a demonstration for the sprint review. Ensure it is appropriate </li>
<li>Prepare any other charts or information radiators helpful for planning, such as a velocity trend chart </li>
<li>Schedule a backlog estimation/refinement meeting as required </li>
</ul>
In sprint planning:<br />
<ul>
<li>Facilitate the entire meeting; take team through all parts </li>
<li>Take notes from the retrospective </li>
<li>Announce velocity in story points from last sprint </li>
<li>Announce team capacity in hours for the next sprint (accommodating vacations, etc) </li>
<li>Post/arrange the product backlog for sprint backlog selection </li>
<li>Keep the team on track for breakdown into tasks; write/post task cards if necessary </li>
<li>Ensure a reality check of task hours vs. capacity happens and adjustments made </li>
</ul>
After sprint planning:<br />
<ul>
<li>Create beginning burndown chart, task board, and any other information radiators </li>
</ul>
Never:<br />
<ul>
<li>Demand that the team take any certain action </li>
<li>Assign work or take unilateral action to change a practice </li>
<li>Neglect Scrum Master duties in favor of "regular work" </li>
</ul>
Once you have the above under control, consider additional duties from the <span style="font-size: medium;"></span><span style="font-size: medium;"><a href="https://www.scrumalliance.org/community/articles/2010/november/an-example-scrummaster-s-checklist" target="_blank">Scrum Master's Checklist</a>.</span></div>
Patrickhttp://www.blogger.com/profile/10070636843509784651noreply@blogger.com0tag:blogger.com,1999:blog-2796910034566353989.post-39754059728154222032015-12-01T12:46:00.000-06:002016-05-06T13:08:47.114-05:00Szalapski.com Scrum Rubric<br />
Agile is a set of values, and does not imply or require certain practices. Scrum is a framework that says a software product should be developed by a self-organizing <b>team</b>, in multiple fixed-length <b>sprints</b>, in full collaboration with the <b>product owner</b>, using <b>Agile</b> values and principles throughout. Scrum specifies a few practices, but as a <b>framework</b>, it is up to the team to insert many other practices into it.<br />
<br />
The following is a guideline on practices to strive for. It is not a maturity model—the goal is not to adopt as many of these practices as possible. It is not a roadmap—do not try to adopt a minimal set of processes at first before adopting others. Instead, it is a menu of options. We recommend you, as a team committed to Scrum, <b>immediately adopt all of sections A, B, and C</b>; adopt as much of section D as you can; and consider adopting any practices in section E.<br />
<br />
Following this rubric is not sufficient; you must learn and apply the concepts and stories of Scrum from training, books, other practitioners, and experience, starting with the <a href="http://www.scrumguides.org/">Scrum Guide</a>. It is possible to adopt most of these practices and still miss the point of Scrum, so do not take this rubric as a gold standard.<br />
<br />
(I offer coaching on all these practices.)<br />
<br />
A. Agile values and principles – If you don’t follow these, then nothing else matters<br />
<ul>
<li>Value <b>people</b> over process</li>
<li>Value <b>working product </b>over documentation</li>
<li>Value <b>collaboration</b> over contracts</li>
<li>Value <b>responding to change</b> over following a plan</li>
<li>A willingness to change any practice, attitude, or relationship that conflicts with the four values</li>
</ul>
B. The price of admission – If you don’t do these, you ain’t doin’ Scrum<br />
<ul>
<li>A commitment from all to follow the Agile values and principles</li>
<li>Empowered, committed, singular, decisive <b>product owner</b> who is always available and interested</li>
<li>Self-organizing <b>team</b> with 2-9 most-time members (do not include 40% or lower allocations)</li>
<li>A designated <b>Scrum Master</b> on the team</li>
<li>Fixed-end sprint iteration of <b>1-4 weeks</b></li>
<li>A <b>product backlog</b> consisting of all the important known work to be done</li>
<li>A <b>sprint planning</b> session starting each sprint</li>
<li>A <b>sprint backlog</b> consisting of all the work to be done in the current sprint</li>
<li>A <b>scrum</b> each day during the sprint; <b>self-assigned</b> tasks</li>
<li>Emphasis in the scrum is on finding and exposing blocks; discussions happen afterward, not during</li>
<li>A commitment to <b>daily adjust</b> based on circumstances</li>
<li>Potentially shippable <b>working software</b> at the end of each sprint</li>
<li>A <b>retrospective</b> meeting every sprint</li>
</ul>
C. Practices strongly urged<br />
<ul>
<li>Estimating product backlog items using <b>planning poker</b> or other system</li>
<li>A formal <b>sprint goal</b></li>
<li>Sprint backlog broken into <b>tasks</b>, estimated in <b>hours remaining</b>, updated daily by the team</li>
<li>Work remaining daily <b>burndown</b> chart</li>
<li>Regular <b>backlog grooming</b> sessions</li>
<li>A well-attended <b>sprint review and demo meeting</b> to stakeholders at or after end of sprint</li>
<li>Sprint planning happens the <b>first day</b> after the end of the previous sprint (<b>no gap</b>)</li>
<li>A shared <b>definition of “done”</b></li>
<li>A short <b>quality</b> plan</li>
<li><b>Automated unit testing </b>(for software)</li>
<li>Zero compiler warning tolerance (for software)</li>
<li>Literally <b>standing up</b> during the scrum</li>
<li><b>Velocity</b> tracking and capacity planning</li>
<li>A “<b>Minimum Viable Product</b> first” attitude</li>
<li>A <b>refactor</b>-as-you-go attitude</li>
<li>A team of generalists and <b>generalizing specialists</b></li>
<li>Steady, sustainable pace; no overtime</li>
<li>Fixed-end sprint iteration of <b>1-3 weeks</b> </li>
</ul>
D. Practices recommended for most Scrum teams<br />
<ul>
<li>A product owner who <b>reports to the business</b>, not to IT</li>
<li>Product owner <b>sits with</b> the team</li>
<li>Most product backlog items in the form of proper <b>user stories</b></li>
<li><b>Swarming</b> on fewer stories at once</li>
<li><b>Spikes</b> whenever needed</li>
<li>A <b>technical debt</b> backlog; a commitment to work on technical debt</li>
<li><b>Continuous integration</b>: Automatic builds, automatic testing, automatic code analysis (for software)</li>
<li>Asynchronous <b>code reviews </b>(for software)</li>
<li><b>Co-located team</b>; common work area/team room</li>
<li><b>Coding standards </b>(for software)</li>
<li>Ad hoc <b>pair programming</b> encouraged (for software)</li>
<li><b>Automated integration tests </b>(for software)</li>
<li><b>Shorter</b> sprints</li>
<li>Manual testers <b>on the team </b>(for software)</li>
</ul>
E. Optional practices to consider for your team<br />
<ul>
<li>User Story mapping</li>
<li>Story brainstorming sessions</li>
<li>Estimation of ROI using planning poker</li>
<li>Test-Driven Development</li>
<li>A burn-up chart of completed vs. remaining work in the backlog</li>
<li>Light-hearted shame trophy for breaking the build</li>
<li>$1 or other light-hearted fine for being late to scrum or other important meetings</li>
<li>Pair programming required</li>
<li>Continuous deployment</li>
<li>Chaos monkey</li>
<li>Advanced training</li>
<li>Automated behavior-driven and/or acceptance tests</li>
<li>Zero bug tolerance</li>
<li>Mandatory fun</li>
<li>Personas</li>
<li>1:1 retrospectives</li>
<li>WIP limit</li>
<li>Reserved time for training/self-education</li>
</ul>
Values to remember throughout<br />
<ul>
<li>The 12 Agile principles</li>
<li>Servant leadership</li>
<li>Self-organizing team</li>
<li>Remove impediments and keep out distractions</li>
</ul>
Patrickhttp://www.blogger.com/profile/10070636843509784651noreply@blogger.com0tag:blogger.com,1999:blog-2796910034566353989.post-53992680366811491922015-08-24T14:23:00.001-05:002015-08-24T14:24:15.690-05:00Coming soon: a rate-limiting and throttling service<div style="text-align: center;">
<span style="font-size: x-large;"><b><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">Limtr</span></b></span></div>
<div style="text-align: center;">
<b><span style="font-size: large;"><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"> </span></span></b></div>
<div style="text-align: center;">
<span style="font-size: medium;"><span style="font-family: Georgia,"Times New Roman",serif;">rate limiting and throttling the simple way</span></span></div>
<div style="text-align: center;">
<span style="font-size: medium;"><span style="font-family: Georgia,"Times New Roman",serif;"><br /></span></span></div>
<div style="text-align: center;">
<span style="font-size: medium;"><span style="font-family: Georgia,"Times New Roman",serif;"><br /></span></span></div>
<div style="text-align: center;">
<span style="font-size: large;"><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"><span style="font-size: medium;"><span style="font-family: Georgia,"Times New Roman",serif;">coming when it's done</span></span></span></span></div>
Patrickhttp://www.blogger.com/profile/10070636843509784651noreply@blogger.com0tag:blogger.com,1999:blog-2796910034566353989.post-46585587098852192252015-04-08T09:00:00.000-05:002015-04-09T12:38:30.377-05:00Which of these things does the arrow belong to? - UX in the wild<span style="font-size: small;">Even simple signs have UX concerns. Take this sign, in San Francisco:</span><br />
<br />
<div class="separator" style="clear: both; text-align: left;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgp5mE9RzbRs9-Gwc9JE-7wcj8q8uJ_dIaEsaFYwpBElqvdOw8csPixIlVQCTUbaZCLERW7MGsIhCVKNzoqQryB87UtbXndmiBwku-gq-iPDAG_vwrdnFX2UmKsUU4EgkDyJ0AYknrUDfvC/s1600/mosconeWestEntry.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgp5mE9RzbRs9-Gwc9JE-7wcj8q8uJ_dIaEsaFYwpBElqvdOw8csPixIlVQCTUbaZCLERW7MGsIhCVKNzoqQryB87UtbXndmiBwku-gq-iPDAG_vwrdnFX2UmKsUU4EgkDyJ0AYknrUDfvC/s1600/mosconeWestEntry.jpg" height="320" width="276" /></a></div>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgEECD8_KEzM5DXrRBMKP8G3VGmlYonhDo3QEGCWi1621IPJ1kxLO3xOxH3_FJjk4EMBF8ToI6q_h8dDyAeji6UoGSbq_Z5PyJCVGRjOsEpJxpkuz3cdcDboT1r0Q7hDOUd0EbHxfk-og5x/s1600/mosconeWestEntry3.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"></a><br /><span style="font-size: small;">I ask you this: does this sign mean the general entry is here, and that the handicap-accessible entry is to your right? Or does it mean the entry is to your right, and it is also handicap accessible?</span><br /><span style="font-size: small;">My first thought was the former. Why? Having the wheelchair symbol and the arrow on the same line implies a stronger relationship than with the information above it. To most people, they don’t think about this directly, but they intuitively process the arrow and what it means.</span><br /><span style="font-size: small;">I looked in my immediate vicinity and did not see any entries. I concluded the meaning must be the latter.</span><br />
<br /><span style="font-size: small;">A better sign would have been:</span><br /><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhmtzRgy_XJFaQz_26YFw9XHzj67S1bZiJTxBcDPpgkeZmowR_jOtf2V13YnZBmDgMytZzjN2XkKcjvbAYC5DZynJlz6G7u3-Tq87w19py6A_r22BGQqRYL2xgRbrx7DJwuwUCyATZmq0Uh/s1600/mosconeWestEntry2.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhmtzRgy_XJFaQz_26YFw9XHzj67S1bZiJTxBcDPpgkeZmowR_jOtf2V13YnZBmDgMytZzjN2XkKcjvbAYC5DZynJlz6G7u3-Tq87w19py6A_r22BGQqRYL2xgRbrx7DJwuwUCyATZmq0Uh/s1600/mosconeWestEntry2.jpg" height="320" width="276" /></a><br /><span style="font-size: small;">Also, if the entry be handicap accessible, there be no need to call that fact out, as it isn’t special. You only need to know where the handicap-accessible entrance is if the main entrance isn’t so accessible. I submit the best sign therefore would be:</span><br />
<span style="font-size: small;"><br /></span>
<div class="separator" style="clear: both; text-align: left;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgEECD8_KEzM5DXrRBMKP8G3VGmlYonhDo3QEGCWi1621IPJ1kxLO3xOxH3_FJjk4EMBF8ToI6q_h8dDyAeji6UoGSbq_Z5PyJCVGRjOsEpJxpkuz3cdcDboT1r0Q7hDOUd0EbHxfk-og5x/s1600/mosconeWestEntry3.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgEECD8_KEzM5DXrRBMKP8G3VGmlYonhDo3QEGCWi1621IPJ1kxLO3xOxH3_FJjk4EMBF8ToI6q_h8dDyAeji6UoGSbq_Z5PyJCVGRjOsEpJxpkuz3cdcDboT1r0Q7hDOUd0EbHxfk-og5x/s1600/mosconeWestEntry3.jpg" height="320" width="276" /></a></div>
<span style="font-size: small;"><br /></span>
<br />
<br />
<br />
<br />
(Of course, the arrow should be centered and not have a blotchy background, but that’s just digital manipulation error. No, I am not going to spend the time to fix it!)<br />
<br />
Bonus UX problem: this entry was on the southeast side of the building. Maybe it should have been called the Moscone Center West South East Entry <a href="https://www.youtube.com/watch?v=NQ-8IuUkJJc#t=0m53">For Kids Who Can't Read Good And Wanna Learn To Do Other Stuff Good Too</a>. <br />
<br />Or, maybe you just shouldn’t name buildings after ordinal directions.Patrickhttp://www.blogger.com/profile/10070636843509784651noreply@blogger.com0tag:blogger.com,1999:blog-2796910034566353989.post-3281943450991776992015-04-06T09:00:00.000-05:002015-04-06T09:00:01.182-05:00We still value the “things on the right”!<p><font size="3">In </font><a href="http://searchsoftwarequality.techtarget.com/opinion/Agile-Manifesto-Both-timeless-and-outdated"><font size="3">a recent article reexamining the Agile Manifesto</font></a><font size="3">, Derwyn Harris criticizes the fourth Agile value, which values responding to change over following a plan: </font><font size="3">“The volume of software being developed today dramatically outpaces that of 2001 and the Agile Manifesto doesn't address that reality. There is a notion in the Manifesto that we're trying to move away from planning, away from negotiation.”</font></p> <p><font size="3">The author only criticizes that we value responding to change more than following a plan. However, she is only criticizing a straw man. Valuing responding to change doesn't mean we avoid planning—a disclaimer right at the bottom of the manifesto itself clarifies this! Responsible Agile teams plan much, perhaps even more than in traditional software development. Agile planning needs to happen throughout product development and to accommodate the more important value of responding to change. </font></p> <p><font size="3">The Agile Manifesto “doesn’t address the reality” of how to plan for complex, fast-moving projects because it was never meant to address it. The Manifesto doesn’t try to give us “values” that we can apply to address every trend and movement of how software is made—it simply provides four values that historically have been secondary and makes them primary. You can—and often should—uphold other values that the manifesto didn’t anticipate. </font></p> <p><font size="3">If Harris means to criticize some naive "Agile" teams for failure to plan, then that is fair game. I would join him!</font></p> Patrickhttp://www.blogger.com/profile/10070636843509784651noreply@blogger.com0tag:blogger.com,1999:blog-2796910034566353989.post-76494482018624954822015-04-01T16:58:00.001-05:002015-04-06T15:11:52.287-05:00Scrum sprint length: three, two, or one week(s)?<span style="color: #4c4c4c; font-size: small;">Suppose you are doing three-week sprints. Should you consider moving to two weeks? If you are already doing two, should you move to one?</span><br />
<span style="color: #4c4c4c; font-size: small;">Some teams want shorter sprints because they need more feedback, a quicker turnaround, and more interaction with the stakeholders.</span><br />
<span style="color: #4c4c4c; font-size: small;">If you are doing Scrum (or even just being Agile) well, you should be able to get lots of feedback during the sprint, regardless of your iteration length. If you feel that this isn't happening, yet you want to stay at longer intervals, consider setting up an informal demo with the Product Owner (PO) and a key stakeholder or user halfway through. This informal demo is flexible to happen more often, as much as you can benefit from it. Remember, official Scrum has the PO as part of the team all day every day. This is not commonly followed, so you have to intentionally look for ways to get that continuous, high-touch feedback from the PO. </span><br />
<span style="color: #4c4c4c; font-size: small;">On the other hand, some teams want shorter sprints in order to improve focus—to truly get done with half the work in half the time. This is the real benefit of one-week sprints; if successful, you can increase your output in that one week due to the benefits of increased focus.</span><br />
<span style="color: #4c4c4c; font-size: small;">Consider one-week sprints if you can possibly deliver genuine value in one week—providing the stakeholders with a potentially-releasable product. Otherwise, I'd keep it at two.</span><br />
<span style="color: #4c4c4c; font-size: small;">Some teams want this benefit of shorter sprints, but are wary of taking the plunge, thinking that the overhead of the Scrum rituals (estimation, sprint planning, daily scrums, and sprint review) is too high to do in one week. </span><br />
<span style="color: #4c4c4c; font-size: small;">I fear this is a straw-man, worry-wart argument. For a one-week sprint for a Scrum-experienced team, I'd hope to do the demo in one hour, the retro in a half-hour, then do estimation (which works best before sprint planning) and sprint planning in a few hours after that. I would combine these all in one day. The team may need to be involved in backlog grooming/story writing later in the sprint, but this shouldn't be the whole team, just individuals as needed. </span><br />
<span style="color: #4c4c4c; font-size: small;">This tighter approach is best done by remembering the goal of sprint planning is to agree on a sprint backlog that the team can begin working on immediately, and nothing more. In my experience, a good morning of sprint planning followed by a team lunch leaves the team extra-productive for the four hours remaining, wherein they get a lot done.</span><br />
<span style="color: #4c4c4c; font-size: small;">I think there is a different obstacle to shorter sprints that we don’t want to admit to ourselves. Too often teams feel two weeks is "easier" than one (And three weeks easier than two) not because it is actually easier, but because there is more time that can be wasted (or stolen by unplanned work) without being noticed. That's a "benefit" of a longer sprint you don't want. All the scrum rituals should take half as long in a one-week sprint as in a two-week sprint, so you should have exactly <a href="http://semperagilis.blogspot.com/2015/02/work-vs-meetings-in-scrum.html">half the time to get done half the work</a>--but hopefully, you will end up able to do a bit more than half the work in a sustainable, fast pace, right? "It’s not enough time" is a smell that the Sprint length is being abused and therefore wasted. </span><br />
<span style="color: #4c4c4c; font-size: small;">In the end, the team should decide 1-, 2-, or 3-week sprints based on how much they can plan with confidence and how much they can benefit from rapid feedback and delivery.</span>Patrickhttp://www.blogger.com/profile/10070636843509784651noreply@blogger.com0tag:blogger.com,1999:blog-2796910034566353989.post-9826922489276418222015-03-27T11:02:00.001-05:002015-04-05T13:26:12.301-05:00Invert your conditionals for better readability<span style="color: black; font-size: small;">You may find code that has an if-else conditional statement that is "in the negative" form. It is almost always easier to understand in the opposite way--in the positive.</span> <br />
<span style="color: black; font-size: small;">BAD (but only a little):</span> <br />
<span style="color: black; font-family: Consolas; font-size: small;"> if (foo != null) <br /> { <br /> // do NORMAL CASE stuff here <br /> } <br /> else <br /> { <br /> // do other stuff here <br /> }</span> <br />
<span style="color: black; font-size: small;">BETTER:</span> <br />
<span style="color: black; font-family: Consolas; font-size: small;"> if (foo == null) <br /> { <br /> // do other stuff here <br /> } <br /> else <br /> { <br /> // do NORMAL CASE stuff here <br /> }</span> <br />
<span style="color: black; font-size: small;">Some would object that, “hey, the normal case stuff should come first”. That is author-centric coding, as if our code must follow our thinking when we wrote it. Instead, we need to have reader-centric coding—let’s make our code as easy as possible for others to understand what it does (and what it is supposed to do). The human mind can conceive of a “if-yes, else-no” pattern much more easily than “if-no, else not-no”.</span><br />
<span style="color: black; font-size: small;">Also, CONSIDER if you can short-circuit away the nesting with a control flow statement such as return, break, or throw. </span><br />
<span style="color: black; font-family: Consolas; font-size: small;"> if (foo == null) <br /> { <br /> // do other stuff here <br /> return; <br /> } <br /> // do NORMAL CASE stuff here</span> <br />
<span style="color: black; font-size: small;">Some advocate a single exit point as a rule, </span><span style="color: black; font-size: small;"><a href="http://stackoverflow.com/questions/36707/should-a-function-have-only-one-return-statement">but I usually do not</a></span><span style="color: black; font-size: small;">. One might think that using if-else is easier to read, but it isn’t—the cognitive load put on the reader by nesting and conditionals is worse than the ability to notice an early exit.</span><br />
Patrickhttp://www.blogger.com/profile/10070636843509784651noreply@blogger.com0tag:blogger.com,1999:blog-2796910034566353989.post-67914228478873457532015-03-03T09:28:00.001-06:002015-04-05T13:21:33.356-05:00Oracle: Making simple things difficult since 1977Oracle is not a company built on a strong UX foundation.<br />
1. They sent me spam, and I wanted to unsubscribe. I click it and get “Step 1” of n.<br />
<a href="http://lh4.ggpht.com/-kZjcXGhujKU/VPXTHMW6AHI/AAAAAAAADSI/hL456jy1Xoo/s1600-h/image%25255B6%25255D.png"><img alt="image" border="0" src="http://lh4.ggpht.com/-BFRFHxTDpf4/VPXTH8jvoVI/AAAAAAAADSM/Oe5QLnM9Ns4/image_thumb%25255B4%25255D.png?imgmax=800" height="390" style="background-image: none; border-bottom: 0px; border-left: 0px; border-right: 0px; border-top: 0px; display: inline; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="image" width="581" /></a><br />
Really? Patrickhttp://www.blogger.com/profile/10070636843509784651noreply@blogger.com0tag:blogger.com,1999:blog-2796910034566353989.post-19183211268390662532015-01-01T08:00:00.000-06:002017-01-10T08:11:51.179-06:00GoogleUserContent test post with imageI hope this image of Sven never goes away, but I fear it might in several months!<a href="https://productforums.google.com/forum/?utm_medium=email&utm_source=footer#!msg/blogger/j7t91E66taw/ku94QCQPDwAJ"> Can you help me figure out why on Google product forums</a>?<br />
<br /><br />
<a href="https://lh3.googleusercontent.com/-wuK4DHOST8E/WHTp06qoErI/AAAAAAAAMuw/fu2L9CFI81Q/s1600-h/svenFull%25255B5%25255D.png"><img alt="svenFull" height="360" src="https://lh3.googleusercontent.com/-BZDEYNyACS4/WHTp1T-wHwI/AAAAAAAAMu0/7mHx9OD7qfo/svenFull_thumb%25255B3%25255D.png?imgmax=800" style="display: inline;" title="svenFull" width="640" /></a>Patrickhttp://www.blogger.com/profile/10070636843509784651noreply@blogger.com0tag:blogger.com,1999:blog-2796910034566353989.post-73838798717241217892014-10-20T07:00:00.000-05:002015-04-05T13:21:49.049-05:00The cracked gasoline dispenser cradle - UX in the wild<span style="font-size: small;"><a href="http://www.nngroup.com/articles/the-need-for-web-design-standards/">Jakob’s Law of Web User Experience</a> states that “users spend most of their time on <i>other</i> web sites.” This can be generalized to the general: users spend most of their time on other systems. Take the gasoline. What do you usually do when you lift the gasoline pump handle off its cradle? Activate the ability to start dispensing fuel by raising the start lever to cover part of the cradle, right? Well, not on all pumps:</span><br />
<a href="http://lh5.ggpht.com/-tP_M_Sh2mpc/VDyhE_7NKCI/AAAAAAAABEU/16E3k8O0Tvc/s1600-h/image%25255B3%25255D.png"><img alt="image" border="0" src="http://lh4.ggpht.com/-TDZuHgvEazc/VDyhFnGxOUI/AAAAAAAABEY/2-F1NY1o4lA/image_thumb%25255B1%25255D.png?imgmax=800" height="316" style="background-image: none; border-bottom-width: 0px; border-left-width: 0px; border-right-width: 0px; border-top-width: 0px; display: block; float: none; margin-left: auto; margin-right: auto; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="image" width="166" /></a><br />
<span style="font-size: small;">This looks a lot like a lever that one should pull up, but it isn’t. You actually have to push a start button that is above and well to the left of where you lifted the pump handle.</span><br />
<span style="font-size: small;">If you look closely, the upper right of the cradle is cracked pretty badly. Clearly, others had the same intuition I had: to yank up on this unyielding cradle. Why? <b>Because I use all other fuel pumps more than this one.</b></span><br />
<span style="font-size: small;"><a href="http://lh5.ggpht.com/-gTI6yK_a2G0/VDyhF_FRyqI/AAAAAAAABEg/czBNsB3tRWM/s1600-h/gas%25255B2%25255D.jpg"><img align="right" alt="gas" border="0" src="http://lh3.ggpht.com/-1qCZ8biAr6s/VDyhGVPFSKI/AAAAAAAABEo/GDMQxgFi36o/gas_thumb.jpg?imgmax=800" height="244" style="background-image: none; border-bottom-width: 0px; border-left-width: 0px; border-right-width: 0px; border-top-width: 0px; display: inline; float: right; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="gas" width="217" /></a>If you are going to make a gas pump cradle with a separate start button, at least make the cradle look nothing like the ones that you pull up! Here’s one.</span><br />
<span style="font-size: small;">Better yet, put the start lever on the bottom cradle—like most other gas pumps.</span>Patrickhttp://www.blogger.com/profile/10070636843509784651noreply@blogger.com0tag:blogger.com,1999:blog-2796910034566353989.post-5286589146311814122014-10-16T18:41:00.001-05:002015-04-05T13:22:08.894-05:00The biggest software conference in the world: Dreamforce closing thoughts<span style="font-size: small;">I’ve been at Dreamforce 2014, the yearly conference for Salesforce.com, all week. With over 120,000 attendees, I’ve never seen an event like this. Just some quick thoughts on Dreamforce:</span><br />
<ul>
<li><span style="font-size: small;">Thanks to all the sponsors, vendors, and partners for doing a good job selling to me without overwhelming me. The conference was already overwhelming; I didn’t need some vendor’s rep I’ve never heard of before giving me some hard sell. Thanks for the t-shirts, USB drives, and most of all, the explanations.</span></li>
<li><span style="font-size: small;">Bringing in Hillary Clinton, Al Gore, Will.i.am Adams, Arianna Huffington, Eckhart Tolle, Neil Young, the Beach Boys, Bruno Mars, and Cake was way over the top and not all that relevant. The Hawaiian greeting was ridiculous.</span></li>
<li><span style="font-size: small;">Also, all the appeals for charity were over the top as well. I could criticize their approach, but I will let <a href="http://www.amazon.com/Waiting-SUPERMAN-Americas-Failing-Participant/dp/B004MKLRRY/ref=sr_1_1?ie=UTF8&qid=1413501884&sr=8-1&keywords=Waiting+For+%22Superman%22%3A+How+We+Can+Save+America%27s+Failing+Public+Schools">Davis Guggenheim</a> and <a href="http://www.amazon.com/Toxic-Charity-Churches-Charities-Reverse/dp/0062076213/ref=sr_1_1?ie=UTF8&qid=1413501932&sr=8-1">Robert D. Lupton</a> do that; besides, I am all in favor of private companies donating in whatever way they choose.</span></li>
<li><span style="font-size: small;">The session speakers, with two exceptions, were excellent and really knew their stuff. A few of the sessions were a bit too basic, which was surprising given how little I know of Salesforce.com.</span></li>
<li><span style="font-size: small;">Much thanks to Salesforce for hiring ample staff. I never was at a loss of where to go or what to do, and there were plenty of people in the sessions, handing out food at meals, and in every other way imaginable.</span></li>
<li><span style="font-size: small;">Also, the check-in system worked great. The staff knew just what to do whenever someone couldn’t get in.</span></li>
<li><span style="font-size: small;">The conference’s size is a weakness. Sessions were a half-mile apart, the keynotes were full a half-hour early, the vendor/partner areas were massive, and the opportunity for personal connections with speakers was limited. </span></li>
<li><span style="font-size: small;">The Dreamforce/Salesforce Success web site works great.</span></li>
<li><span style="font-size: small;">The official WiFi was surprisingly good everywhere—it almost always worked. I can’t imagine the work that goes into such a IT effort.</span></li>
<li><span style="font-size: small;">There needs to be a better way to choose sessions. The Agenda Builder online lets me filter and search, but that doesn’t do me much good when I am browsing and really have no idea what to pick. Maybe a system with a bit more guidance and direction is in order?</span></li>
</ul>
<span style="font-size: small;">All in all, it is impressive that Salesforce.com can pull off such a large conference. Overall, if you are into Salesforce.com, this conference really does a good job in a lot of ways—including its value for the price. If you are learning Salesforce from scratch, the online tutorials are a better place to start. There are many; <a href="http://ccoenraets.github.io/salesforce-developer-fasttrack/">here’s one</a>. </span><br />
<span style="font-size: small;">I don’t expect to go back to Dreamforce—but I am curious as to how big it can get!</span>Patrickhttp://www.blogger.com/profile/10070636843509784651noreply@blogger.com0