<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:blogger='http://schemas.google.com/blogger/2008' xmlns:georss='http://www.georss.org/georss' xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-10846234</id><updated>2025-12-22T11:12:56.948+00:00</updated><category term="processes"/><category term="development"/><category term="management"/><category term="Principles"/><category term="Oracle"/><category term="Javascript"/><category term="Open Source"/><category term="design"/><category term="P-dd"/><category term="Windows"/><category term="cvs"/><category term="elegent"/><category term="estimation"/><category term="hack"/><category term="planning"/><category term="promises"/><category term="salesforce"/><category term="showing off"/><category term="wtf"/><title type='text'>BOBABLOG: Agile software development and Salesforce</title><subtitle type='html'>More than 2 decades of writing software, and still loving it...</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default?alt=atom'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default?alt=atom&amp;start-index=26&amp;max-results=25'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJKWDfU-frt3gi_ubtgJ8msvcKcp6ChPvxN_uLMCR-FqygXX-IabdD7vmbQ3RHXnwvmQRPaiXacC6CoIfaYcyh0hGdV0PQzbjFaqf_ECJjuD2JzQVdrINY6YBtjgISt_4/s113/Rob+Baillie.png'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>151</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-10846234.post-6989048479596688864</id><published>2018-12-27T13:11:00.003+00:00</published><updated>2018-12-28T10:20:37.957+00:00</updated><title type='text'>Lightning Web Components - Conditional formatting and iterators</title><content type='html'>&lt;p&gt;One of the really elegant parts of Lightning Components was the ability to conditionally apply classes based on data.&lt;/p&gt;

&lt;p&gt;This is something that is no longer available to us, as the expressions we are allowed to put into templates are now limited to either:
&lt;ul&gt;
  &lt;li&gt;A property of the Javascript class (or a sub-property that can be referenced through a top level property).&lt;/li&gt;
  &lt;li&gt;A getter, that accepts no parameters.&lt;/li&gt;
&lt;/ul&gt;
&lt;/p&gt;

&lt;p&gt;I’ve already mentioned a change in this area &lt;a href=&quot;http://robertbaillie.blogspot.com/2018/12/lightning-web-components-api-slots-and.html&quot;&gt;in this blog post about building re-usable components&lt;/a&gt;, but I felt it was time to drill into this just a little further...&lt;/p&gt;

&lt;div class=&quot;aside&quot;&gt;&lt;h3&gt;Update - 28/12/2018&lt;/h3&gt;As seems to be increasingly the case, as soon as I posted this I was corrected on a couple of points.  I&#39;ve since updated the post to include that feedback.  Hopefully the result isn&#39;t too confusing!&lt;/div&gt;

&lt;h1&gt;The scenario&lt;/h1&gt;

&lt;p&gt;Let’s say we want to render a list of objects.  There’s a flag &#39;isSelected&#39; on each of the records, and if that flag is set we want to change the rendering for that particular record.&lt;p&gt;

&lt;h3&gt;JSON data:&lt;/h3&gt;
&lt;pre class=&quot;brush:js;&quot;&gt;
[ { &quot;id&quot; : 1
  , &quot;name&quot; : &quot;Anne&quot;
  , &quot;isSelected&quot; : false },
  { &quot;id&quot; : 2
  , &quot;name&quot; : &quot;Bob&quot;
  , &quot;isSelected&quot; : true },
  { &quot;id&quot; : 3
  , &quot;name&quot; : &quot;Carla&quot;
  , &quot;isSelected&quot; : true }
]
&lt;/pre&gt;

&lt;h3&gt;Required Output:&lt;/h3&gt;
&lt;pre class=&quot;brush:html;&quot;&gt;
&amp;lt;ul&gt;
  &amp;lt;li class=&quot;record&quot;&gt;Anne&amp;lt;/li&gt;
  &amp;lt;li class=&quot;selected record&quot;&gt;Bob&amp;lt;/li&gt;
  &amp;lt;li class=&quot;selected record&quot;&gt;Carla&amp;lt;/li&gt;
&amp;lt;/ul&gt;
&lt;/pre&gt;

&lt;h1&gt;Lightning Component&lt;/h1&gt;

&lt;p&gt;In a Lightning Component, this would be near trivial, as we could use a ternary operator in the template to render the inclusion of the &#39;selected&#39; class conditionally.&lt;/p&gt;

&lt;pre class=&quot;brush:html;&quot;&gt;
&amp;lt;ul&gt;
  &amp;lt;aura:iteration items=&quot;{!v.records}&quot; var=&quot;thisRecord&quot;&gt;
    &amp;lt;li class=&quot;{!(thisRecord.isSelected?&#39;selected&#39;:&#39;&#39; ) + &#39; record&#39; }&quot;&gt;{!thisRecord.name}&lt;/li&gt;
  &amp;lt;/aura&gt;
&amp;lt;/ul&gt;
&lt;/pre&gt;

&lt;p&gt;The reason this is so simple, is that we could put proper expressions into our replacements in Lightning Components, giving us fantastic flexibility in the output for each individual attribute.&lt;/p&gt;

&lt;p&gt;Unfortunately (for this case), this isn&#39;t possible in Lightning Web Components...&lt;/p&gt;

&lt;h1&gt;Lightning Web Component&lt;/h1&gt;

&lt;p&gt;First up, let&#39;s just clarify what we mean when we say we can no longer do the string concatenation, or the ternary operator in an attribute expression, &lt;a href=&quot;http://robertbaillie.blogspot.com/2018/12/lightning-web-components-api-slots-and.html&quot;&gt;as I detailed in my earlier post.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What we mean is, we can’t do the following:&lt;/p&gt;

&lt;pre class=&quot;brush:html;&quot;&gt;
&amp;lt;ul&gt;
  &amp;lt;template for:each={records} for:item=&quot;thisRecord&quot;&gt;
    &amp;lt;li class=&quot;{thisRecord.isSelected?&#39;selected&#39;:&#39;&#39; + &#39; record&#39; }&quot;&gt;{thisRecord.name}&amp;lt;/li&gt;
  &amp;lt;/template&gt;
&amp;lt;/ul&gt;
&lt;/pre&gt;

&lt;p&gt;All we can do is reference a single value from our data held against our Javascript object, or call a getter against it.  E.g. (not that &lt;i&gt;this&lt;/i&gt; template is of much use to us right now)&lt;/p&gt;

&lt;pre class=&quot;brush:html;&quot;&gt;
&amp;lt;ul&gt;
  &amp;lt;template for:each={records} for:item=&quot;thisRecord&quot;&gt;
    &amp;lt;li class={thisRecord.isSelected}&gt;{thisRecord.name}&amp;lt;/li&gt;
  &amp;lt;/template&gt;
&amp;lt;/ul&gt;
&lt;/pre&gt;

&lt;p&gt;OK - so what other options do we have?&lt;/p&gt;

&lt;h2&gt;Option 1 - Build your class lists in your data:&lt;/h2&gt;

&lt;p&gt;So, we could build up the list of classes that we want to render against each record in our data - once the data is populated from where-ever, we can loop over the records and update the data so we end up with something like:&lt;/p&gt;

JSON data:
&lt;pre class=&quot;brush:js;&quot;&gt;
[ { &quot;id&quot; : 1
  , &quot;name&quot; : &quot;Anne&quot;
  , &quot;isSelected&quot; : false
  , &quot;classes&quot; : &quot;record&quot; },
  { &quot;id&quot; : 2
  , &quot;name&quot; : &quot;Bob&quot;
  , &quot;isSelected&quot; : true
  , &quot;classes&quot; : &quot;selected record&quot; },
  { &quot;id&quot; : 3
  , &quot;name&quot; : &quot;Carla&quot;
  , &quot;isSelected&quot; : true
  , &quot;classes&quot; : &quot;selected record&quot; }
]
&lt;/pre&gt;

&lt;p&gt;We can then render the required output like this:&lt;/p&gt;

&lt;pre class=&quot;brush:html;&quot;&gt;
&amp;lt;ul&gt;
  &amp;lt;template for:each={records} for:item=&quot;thisRecord&quot;&gt;
    &amp;lt;li key={thisRecord.id} class={thisRecord.classes}&gt;{thisRecord.name}&amp;lt;/li&gt;
  &amp;lt;/template&gt;
&amp;lt;/ul&gt;
&lt;/pre&gt;

&lt;h3&gt;Pros:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;The template is simple.&lt;/li&gt;
&lt;li&gt;Since we&#39;re processing in Javascript, we can draw on &lt;i&gt;any&lt;/i&gt; information and make whatever complex rules we want.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;Cons:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;We need to process the data after we’ve built it.  This means we can no longer use a wired property, and need to use either a wired function or imperatively called Apex.  This may not be a big deal, but it&#39;s worth noting.
&lt;li&gt;Data retrieved from Apex is immutable, so if this is the only thing we need to add to the data, then may find that we need to copy data into new objects, or add a new data structure in order to get the classes property added - that can be a pain.&lt;/li&gt;
&lt;li&gt;The logic for the classes that each record should have assigned is held in Javascript.  I recognise that this is the direction we seem to be going, but I still like to avoid this where possible.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;Option 2 - Use a template &#39;if&#39; and repeat the li tag.&lt;/h2&gt;

&lt;p&gt;If we want to avoid doing anything complex in our Javascript, we can add template &#39;if&#39;s into the markup, and conditionally render the &amp;lt;li&gt; tag in its two different forms.&lt;/p&gt;

&lt;p&gt;For example, we could do the following:&lt;/p&gt;

&lt;pre class=&quot;brush:html;&quot;&gt;
&amp;lt;ul&gt;
  &amp;lt;template for:each={records} for:item=&quot;thisRecord&quot;&gt;
    &amp;lt;template if:true={thisRecord.isSelected}&gt;
      &amp;lt;li key={thisRecord.id} class=&quot;selected record&quot;&gt;{thisRecord.name}&amp;lt;/li&gt;
    &amp;lt;/template&gt;
    &amp;lt;template if:false={thisRecord.isSelected}&gt;
      &amp;lt;li key={thisRecord.id} class=&quot;record&quot;&gt;{thisRecord.name}&amp;lt;/li&gt;
    &amp;lt;/template&gt;
  &amp;lt;/template&gt;
&amp;lt;/ul&gt;
&lt;/pre&gt;

&lt;h3&gt;Pros:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;The Javascript doesn&#39;t contain any of the logic for the conditional rendering.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;Cons:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;We&#39;re breaking the &quot;Don&#39;t repeat yourself&quot; (DRY) principle, and repeating the structure of the &amp;lt;li&gt; tag in each side of the IF condition. 
 In this simple case this may not seem like a big deal, but still - any change to that rendering now needs to be made in 2 places, instead of 1. 
 And let&#39;s be honest, how often is the case this simple?  We&#39;ll probably find that we have to copy a LOT of the template to work like this.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;Option 3 - Use a template &#39;if&#39;, and change our CSS.&lt;/h2&gt;

&lt;p&gt;Another alternative is to use the template if, but to isolate the part that changes from the part that doesn&#39;t.  That is, we introduce HTML inside our &#39;if:true&#39; that &lt;i&gt;only&lt;/i&gt; exists in order to apply the formatting that should be assigned to the &#39;isSelected&#39; records.&lt;/p&gt;

&lt;p&gt;That is, we do the following in our template, to introduce a new, standalone, div that has the &#39;selected&#39; class applied, and then wrap the content of the &amp;lt;li&gt; in another div.&lt;p&gt;

&lt;pre class=&quot;brush:html;&quot;&gt;
&amp;lt;ul&gt;
  &amp;lt;template for:each={records} for:item=&quot;thisRecord&quot;&gt;
    &amp;lt;li key={thisRecord.id} class=&quot;record&quot;&gt;
        &amp;lt;template if:true={thisRecord.isSelected}&gt;
            &amp;lt;div class=&quot;selected&quot;&gt;&amp;lt;/div&gt;
        &amp;lt;/template&gt;
        &amp;lt;div&gt;{thisRecord.name}&amp;lt;/div&gt;
    &amp;lt;/li&gt;
&amp;lt;/ul&gt;
&lt;/pre&gt;

&lt;p&gt;Having done this, we can use more advanced CSS selectors to apply our &#39;selected&#39; style to the div that follows the div with &#39;selected&#39; as its class.&lt;/p&gt;

&lt;p&gt;For example, let&#39;s say our &#39;selected&#39; records should have a green border:&lt;/p&gt;

&lt;pre class=&quot;brush:html;&quot;&gt;
.selected+div {
    border: 1px solid green;
}
&lt;/pre&gt;

&lt;p&gt;The selector &#39;.selected+div&#39; means &#39;The div that follows the tag with the class &#39;selected&#39;.&lt;p&gt;

&lt;p&gt;You can &lt;a href=&quot;https://www.w3schools.com/csSref/css_selectors.asp&quot;&gt;read about CSS Selectors here.&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;Pros:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;We conditionally render only the addition of the class in the template - nothing is repeated.&lt;/li&gt;
&lt;li&gt;The Javascript doesn&#39;t contain any of the logic for the conditional rendering.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;Cons:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;We need to introduce additional structure into the HTML that exists purely to apply classes to other elements.  This isn&#39;t ideal and can change the behaviour of other classes further down the structure (e.g. we have introduced a div here - what impact does that have?)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;Option 4 - Introduce a sub component.&lt;/h2&gt;

&lt;p&gt;It would be good if we could call a getter function at run time in order to get the list of classes, along the lines of &lt;a href=&quot;http://robertbaillie.blogspot.com/2018/12/lightning-web-components-api-slots-and.html&quot;&gt;the component described in the earlier post.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The problem is that we can&#39;t call a function and pass it the context of the individual record that we are rendering.&lt;/p&gt;

&lt;p&gt;So does that mean we can&#39;t call a function?&lt;/p&gt;

&lt;p&gt;No, it just means that we need to narrow the context of the component down into each record before we call the function - and we can do that with a sub-component that just renders the &amp;lt;li&gt;.&lt;/p&gt;

&lt;p&gt;We can call our sub-component (recordRenderer) with something like this:&lt;/p&gt;
&lt;pre class=&quot;brush:html;&quot;&gt;
&amp;lt;ul&gt;
  &amp;lt;template for:each={records} for:item=&quot;thisRecord&quot;&gt;
    &amp;lt;c-record-renderer key={thisRecord.id} record={thisRecord}&gt;&amp;lt;/c-record-renderer&gt;
  &amp;lt;template&gt;
&amp;lt;/ul&gt;
&lt;/pre&gt;

&lt;p&gt;Our sub-component template can be:&lt;/p&gt;

&lt;pre class=&quot;brush:html;&quot;&gt;
  &amp;lt;li class={classes}&gt;{record.name}&amp;lt;/li&gt;
&lt;/pre&gt;

&lt;p&gt;And our sub-component javascript can be:&lt;/p&gt;

&lt;pre style=&quot;brush:js;&quot;&gt;
    import { LightningElement, api } from &#39;lwc&#39;;

    export default class RecordRenderer extends LightningElement {

        @api record;

        get classes() {
            if ( this.record.isSelected ) {
                return &#39;selected record&#39;;
            }
            return &#39;record&#39;
        }
    }
&lt;/pre&gt;

&lt;h3&gt;Pros:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Both the template for the parent and sub component are very simple and focused on small aspects of the rendering - no IFs in the templates is a good thing IMO.&lt;/li&gt;
&lt;li&gt;It&#39;s very possible that there will be more complex behaviour required in the future, and having the context set to be the individual record could make that behaviour much simpler to implement in the future.  For example, passing data into events due to a &#39;click&#39; event could be well served if we have that distinction between the parent and child components and context.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;Cons:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;OK, we have the classes logic inside the Javascript, much like in Option 1, but we don&#39;t have to process the data - the getter is called at render time.  And, presumably the code in the renderer is near trivial, so maybe that&#39;s not such a big problem.&lt;/li&gt;
&lt;li&gt;Update - 28/12/2018 - Unfortunately though, in this case, we generated non-valid HTML.  Since we have introduced a new component, we have also introduced a new tag into our generated HTML.  The result is something along the lines of ul -&gt; c-record-renderer -&gt; li, and this is not strictly valid.  We should certainly bear this in mind when looking at individual situations.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;Conclusions&lt;/h1&gt;

&lt;div class=&quot;aside&quot;&gt;&lt;h3&gt;Update 28/12/2018&lt;/h3&gt;The conclusions were re-written after the feedback that you can find in the comments below, and some more thought on the topic.&lt;/div&gt;

&lt;p&gt;The limitations of the expressions allowed in templates makes for a less elegant solution to this kind of problem.  However, that&#39;s not an altogether terrible thing.  Sometimes you can have too much power and too many choices.  Limitations can allow us to define recognised best-practices that are harder to break away from.&lt;/p&gt;

&lt;p&gt;Even with those limitations, there are many different ways in which we can implement any required functionality, and even this simple case has 4 options of unequal merit&lt;/p&gt;

&lt;p&gt;So which way should we go?&lt;/o&gt;

&lt;p&gt;Well, that depends on the precise scenario, and in this case we have defined something precise, but potentially a little too simplistic to define a clear best-practice.&lt;/p&gt;

&lt;p&gt;I would find it hard to argue that Option 2 (repetition in the template) is ever worth the pain - repeating chunks of template or code is rarely a good idea, even in the simplest of cases.&lt;/p&gt;

&lt;p&gt;Option 1 (pre-process the data) has its merits, and as Caridy has responded below, is the recommended approach from Salesforce - that&#39;s not to be sniffed at.  However, if the only reason you would be processing the data is to add a list of classes to it, I&#39;d suggest that it shouldn&#39;t be your immediate &#39;go-to&#39; solution.&lt;/p&gt;

&lt;p&gt;My initial reaction to Option 3 (advanced CSS selectors) was that it was a decent one with few drawbacks, although the more I look at it, the more I dislike the fact that we are breaking the semantics of the HTML document we are generating.  We introduce structure who&#39;s only purpose is to define the rendering behaviour of &lt;i&gt;another&lt;/i&gt; component.  That&#39;s not really how HTML is &lt;i&gt;supposed&lt;/i&gt; to work.  Ideally we have meaning in our structure, and this structure expresses no meaning.&lt;/p&gt;

&lt;p&gt;So, even though Option 3 may often be straightforward, I&#39;d probably shy away from it now.  Just because CSS selectors can be used in this way, doesn&#39;t mean that they should.&lt;/p&gt;

&lt;p&gt;Option 4 (decompose the component) doesn&#39;t really work in this case - it generates non-valid HTML.  But that doesn&#39;t mean it never works.  There will be plenty of similar scenarios where the resulting HTML is perfectly valid.  In fact, I&#39;m sure we&#39;ll often find scenarios where the sub-component we produce will be a re-usable component that we can drop into other scenarios easily.  In &lt;i&gt;that&lt;/i&gt; situation we&#39;ll surely want to decompose instead?&lt;/p&gt;

&lt;p&gt;So where does that leave us?&lt;/p&gt;

&lt;p&gt;Simply put - there&#39;s no clear answer, as is generally the case - the technique we use should be appropriate for the scenario we&#39;re implementing - though options 2 and 3 will rarely be appropriate..&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If there are other good reasons to split some of the rendering into a sub-component, then splitting it out is probably a good idea - but if that results in non-valid HTML, then it&#39;s a bad idea.&lt;/li&gt;
&lt;li&gt;If there are other reasons to pre-process and re-shape the data, then adding the class definition to that processing is probably a good idea - but if it forces you to clone records and re-shape them &lt;i&gt;just&lt;/i&gt; to add a class, then maybe it&#39;s not the best idea.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I definitely take the point from Salesforce that the latter is their preferred route, but my natural instinct is pretty much always to reduce processing and make smaller things.  It&#39;ll be interesting to see how much my opinion changes as I build real applications with Lightning Web Components.  Based on how much my opinion has changed in the last 24 hours - I expect it to change a lot!&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/6989048479596688864/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/10846234/6989048479596688864' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/6989048479596688864'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/6989048479596688864'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2018/12/lightning-web-components-conditional.html' title='Lightning Web Components - Conditional formatting and iterators'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJKWDfU-frt3gi_ubtgJ8msvcKcp6ChPvxN_uLMCR-FqygXX-IabdD7vmbQ3RHXnwvmQRPaiXacC6CoIfaYcyh0hGdV0PQzbjFaqf_ECJjuD2JzQVdrINY6YBtjgISt_4/s113/Rob+Baillie.png'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-7786793954250386040</id><published>2018-12-20T15:13:00.000+00:00</published><updated>2018-12-22T19:44:40.184+00:00</updated><title type='text'>Lightning Web Components - Events, and listening to your children</title><content type='html'>&lt;div class=&quot;aside&quot;&gt;
&lt;p&gt;&lt;b&gt;Update - 22/12/2018&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;Thanks to an anonymous comments poster from inside Salesforce, I&#39;ve come to learn that this post contained some inaccuracies - a typo in some code, a forgotten parameter to &#39;bind&#39; that made an example more complex than it needed to be, and a lack of experience with Shadow DOMs that led to a failure in event propagation.&lt;/p&gt;
&lt;p&gt;I&#39;m massively grateful to the poster for pointing out my errors, and helping to learn a little more about the capabilities.  I&#39;ve left the original text intact and annotated where appropriate, to correct my errors - except the typo in the example - I&#39;ve fixed that.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Another fantastic inclusion in Lightning Web Components is the completely reworked events model.&lt;/p&gt;

&lt;p&gt;De-composing functionality and building smaller, and more generic building blocks has become much simpler and much more intuitive.&lt;/p&gt;

&lt;p&gt;In the world of Lightning Components I never got on with events.  The idea of adding a Salesforce configuration for an event, registering events on the dispatcher template, and then registering listeners on the receiving template seemed really cumbersome.  And then added onto that was the differences in syntax between component and application events.  They just felt really unnatural.&lt;/p&gt;

&lt;p&gt;In Lightning Web Components all this has become significantly simpler, and much more in-keeping with the standard HTML / Javascript model.&lt;/p&gt;

&lt;p&gt;We&#39;ve already seen how we can use @api allow state to be passed into our components.  Now we&#39;re talking about notifying our parents when events occur.&lt;/p&gt;

&lt;p&gt;I &lt;i&gt;could&lt;/i&gt; go into deep detail on how this is done, but &lt;a href=&quot;https://developer.salesforce.com/docs/component-library/documentation/lwc/lwc.events&quot;&gt;the documentation on this area is spot on&lt;/a&gt;, and there&#39;s no need to repeat it - follow the guide in the docs and you can&#39;t go far wrong.  It&#39;s particularly well written and introduces the concept brilliantly.&lt;/p&gt;

&lt;p&gt;That said, there has to be something to say, right?&lt;/p&gt;

&lt;p&gt;Well, yes, and before I go into some of the less obvious limitations, let&#39;s just present a simple example:

&lt;ul&gt;
  &lt;li&gt;In the child component, we create and dispatch an event.&lt;/li&gt;
  &lt;li&gt;When you include the child component, specify the handler for the event&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;Something along the lines of:&lt;/p&gt;

&lt;h3&gt;Child component&#39;s Javascript&lt;/h3&gt;
&lt;pre style=&quot;brush:js&quot;&gt;
import { LightningElement, track } from &#39;lwc&#39;;

export default class ChildComponent extends LightningElement {

    @track value;

    // Called from the onchange handler on an input
    handleValueChanged( event ) {
        this.value = event.target.value;
        this.dispatchEvent( new CustomEvent( &#39;valuechanged&#39;, { detail: this.value } ) );
    }
}
&lt;/pre&gt;

&lt;h3&gt;Parent component&#39;s template&lt;/h3&gt;
&lt;pre style=&quot;brush:html&quot;&gt;
    &amp;lt;c-child-component onvaluechanged={handleOnValueChanged}&gt;&lt;/c-child-component&gt;
&lt;/pre&gt;

&lt;h3&gt;Parent component&#39;s Javascript&lt;/h3&gt;
&lt;pre style=&quot;brush:js&quot;&gt;
import { LightningElement, track } from &#39;lwc&#39;;

export default class ParentComponent extends LightningElement {

    @track updatedValue;

    handleOnValueChanged( event ) {
        this.updatedValue = event.detail;
    }
}
&lt;/pre&gt;

&lt;p&gt;OK.  So how simple is that?  No Salesforce configuration to create, nice simple syntax, event handlers defined in the template, exactly the same way you would if it was a standard HTML tag&lt;/p&gt;

&lt;p&gt;Without wanting to repeat the documentation from Salesforce, it&#39;s worth calling out a few important points:
&lt;ul&gt;
  &lt;li&gt;dispatchEvent and CustomEvent are standard Javascript.&lt;/li&gt;
  &lt;li&gt;When you include the child component, you specify the handler for the event in the template.&lt;/li&gt;
  &lt;ul&gt;
    &lt;li&gt;The event should not start with &#39;on&#39;, and the attribute you assign the handler to will have &#39;on&#39; appended to the start.&lt;/li&gt;
    &lt;li&gt;The fact we can specify the handler as &#39;onvaluechanged&#39; when we create the tag is LWC specific, and for very good reason (explained later).  You cannot do this with standard Web Components.&lt;/li&gt;
  &lt;/ul&gt;
  &lt;li&gt;We can pass data from the child component in the event, by passing an object as the second parameter.&lt;/li&gt;
  &lt;ul&gt;
    &lt;li&gt;Note that the data can &lt;b&gt;only&lt;/b&gt; be in the &#39;detail&#39; property.  If you add data to any other property you may accidentally overwrite a standard property, and if you don&#39;t use another standard property it won&#39;t be visible in the event anyway - you put data into &#39;detail&#39;, and that&#39;s all you have.  Live with it.&lt;/li&gt;
    &lt;li&gt;You &lt;i&gt;can&lt;/i&gt; pass an object, but if you do you should construct it there and then.  But you probably shouldn&#39;t.&lt;/li&gt;
  &lt;/ul&gt;
&lt;/ul&gt;
&lt;/p&gt;

&lt;p&gt;OK, that&#39;s all well and good - but where are the limitations?&lt;/p&gt;

&lt;p&gt;Well, the main one I&#39;ve found was a real surprise to me - to the point that I&#39;m worried that I&#39;ve misunderstood something.&lt;/p&gt;

&lt;div class=&quot;aside&quot;&gt;
&lt;p&gt;&lt;b&gt;Update - 22/12/2018&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;I did misunderstand something, so the next few statements are wrong.  This isn&#39;t a limitation of LWC, nor even a Salesforce specific behaviour - this was me not understanding the shadow DOM model, and missing a &lt;a href=&quot;https://developer.salesforce.com/docs/component-library/documentation/lwc/lwc.events_propagation&quot;&gt;vital piece of documentation&lt;/a&gt;.  I&#39;ve left it here for completion&#39;s sake only.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;In the standard Javascript events model - all events propagate to all levels.&lt;/p&gt;
&lt;p&gt;For example, if I have the following HTML:&lt;/p&gt;

&lt;pre style=&quot;brush:html&quot;&gt;
    &amp;lt;div id=&quot;grandparent&quot; onchange=&quot;handleChange();&quot;&gt;
        &amp;lt;div id=&quot;parent&quot;&gt;
            &amp;lt;div id=&quot;child&quot;&gt;
                &amp;lt;input onchange=&quot;handleChange();&quot;/&gt;
            &amp;lt;/div&gt;
        &amp;lt;/div&gt;
    &amp;lt;/div&gt;
&lt;/pre&gt;

&lt;p&gt;When the value of the input changes, the onchange event is handled by &lt;b&gt;both&lt;/b&gt; the onchange handler on the input and the &#39;grandparent&#39; div.  Events propagate through the whole DOM, unless a handler stops it by calling &#39;stopPropogation&#39; against the event.&lt;/p&gt;
&lt;p&gt;It&#39;s generally recognised that this is a good thing, and that events should &lt;b&gt;not&lt;/b&gt; be stopped unless there&#39;s very good reason.

&lt;p&gt;However, as far as I can see, this is not true when you cross boundaries between LWCs. (Update 22/12/2018 - it IS true, but different, and explained later).&lt;/p&gt;

&lt;p&gt;For example, if I had the above example for a child component, and included it in a parent as such:&lt;/p&gt;

&lt;h3&gt;Parent component&#39;s template&lt;/h3&gt;
&lt;pre style=&quot;brush:html&quot;&gt;
    &amp;lt;c-child-component onvaluechanged={handleOnValueChanged}&gt;&lt;/c-child-component&gt;
&lt;/pre&gt;

&lt;p&gt;And then included that in the grandparent as such:&lt;/p&gt;

&lt;h3&gt;Grandparent component&#39;s template&lt;/h3&gt;
&lt;pre style=&quot;brush:html&quot;&gt;
    &amp;lt;c-parent-component onvaluechanged={handleOnValueChanged}&gt;&lt;/c-parent-component&gt;
&lt;/pre&gt;

&lt;p&gt;Assuming that the parent component does not raise a &#39;valuechanged&#39; event of its own, the &#39;onvaluechanged&#39; handler on the grandparent component will never get called.&lt;p&gt;

&lt;p&gt;It seems that you can only handle a component&#39;s event in its parent&#39;s scope. (Update 22/12/2018 - Not true, you &lt;b&gt;can&lt;/b&gt; push events through the boundary, and how is explained later)&lt;p&gt;

&lt;div class=&quot;aside&quot;&gt;
&lt;p&gt;Note: these are actually slightly different scenarios I&#39;m explaining, but I think it&#39;s worthwhile in order to illustrate the point.  Also, there &lt;b&gt;is&lt;/b&gt; a &#39;bubbles&#39; property that you can set on the CustomEvent when you create it, although I didn&#39;t see a change in behaviour when I did that.&lt;/p&gt;
&lt;p&gt;As I&#39;ve said, I&#39;m surprised by this behaviour, so am happy to be told I&#39;m wrong, and learn where my mistake is.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Update - 22/12/2018&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;And it turns out, I HAVE been told where my mistake was, and so the following section has now been added.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;It turns out that this behaviour is the &lt;i&gt;default&lt;/i&gt; for events, but that it can be overridden.  The &lt;a href=&quot;https://developer.salesforce.com/docs/component-library/documentation/lwc/lwc.events_propagation&quot;&gt;mechanism for that is detailed here&lt;/a&gt;, and again, this is standard Javascript behaviour &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent&quot;&gt;as detailed here.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In essence, what we have to do is define the event as one that both &#39;bubbles&#39; and is &#39;composed&#39;.  The former means that it will travel upwards through the DOM, a resulting effect being that multiple event sources can be handled by the same handler.  The latter means that it will cross the boundary between the Shadow DOM (the component that raised it), and into the next (the component that included the one that raised it), and then keep going all the way to the document root.&lt;/p&gt;

&lt;h3&gt;Child component&#39;s Javascript becomes&lt;/h3&gt;
&lt;pre style=&quot;brush:js&quot;&gt;
import { LightningElement, track } from &#39;lwc&#39;;

export default class ChildComponent extends LightningElement {

    @track value;

    // Called from the onchange handler on an input
    handleValueChanged( event ) {
        this.value = event.target.value;
        this.dispatchEvent( new CustomEvent( &#39;valuechanged&#39;, { detail: this.value, bubbles: true, composed: true } ) );
    }
}
&lt;/pre&gt;

&lt;p&gt;This is all very well explained in the documentation, and so you should read it carefully to understand it.&lt;/p&gt;

&lt;p&gt;Bubbling and Composed Events &lt;b&gt;are&lt;/b&gt; extremely powerful elements, and allow you to perform some complex event management and (for example) react in many different ways to the same single event being fired.  However, with that power comes great responsibility.  The more &#39;global&#39; in nature your events are, the harder they are to debug and the more care you need to take in naming them.  I think I&#39;m with Salesforce on this one - decompose your components and keep your events as local as possible.&lt;/p&gt;

&lt;h2&gt;Adding an event handler via Javascript&lt;/h2&gt;

&lt;p&gt;So what of the &#39;on&#39; behaviour? Why is this &lt;b&gt;such&lt;/b&gt; a cool addition?&lt;/p&gt;

&lt;p&gt;Well, that&#39;s best explained by illustrating what we would need to do if this wasn&#39;t available to us.&lt;/p&gt;

&lt;p&gt;Let&#39;s go back to our child component&lt;/p&gt;
&lt;h3&gt;Child component&#39;s Javascript&lt;/h3&gt;
&lt;pre style=&quot;brush:js&quot;&gt;
import { LightningElement, track } from &#39;lwc&#39;;

export default class ChildComponent extends LightningElement {

    @track value;

    // Called from the onchange handler on an input
    handleValueChanged( event ) {
        this.value = event.target.value;
        this.dispatchEvent( new CustomEvent( &#39;valuechanged&#39;, { detail: this.value } ) );
    }
}
&lt;/pre&gt;

&lt;p&gt;It dispatches a &#39;valuechanged&#39; event that we can handle in a parent component.&lt;/p&gt;

&lt;p&gt;We include the child component with a simple node:
&lt;h3&gt;Parent component&#39;s template&lt;/h3&gt;
&lt;pre style=&quot;brush:html&quot;&gt;
    &amp;lt;c-child-component&gt;&amp;lt;/c-child-component&gt;
&lt;/pre&gt;

&lt;p&gt;Note we are no longer setting onvaluechanged because, in our hypothetical scenario, this is not possible.&lt;/p&gt;

&lt;p&gt;Now, in order to handle the event we need to attach a handler to the component in our parent component&#39;s Javascript.&lt;/p&gt;

&lt;p&gt;First we need to find it, so we set a property on the component that we can use to retrieve it.  You &lt;b&gt;may&lt;/b&gt; default to setting an &#39;id&#39;, but it turns out that Salesforce will adjust the ids on nodes, so we can&#39;t rely on that.  Instead, we decide to set a class:&lt;/p&gt;

&lt;h3&gt;Parent component&#39;s template&lt;/h3&gt;
&lt;pre style=&quot;brush:html&quot;&gt;
    &amp;lt;c-child-component class=&quot;child&quot;&gt;&amp;lt;/c-child-component&gt;
&lt;/pre&gt;

&lt;p&gt;Now, the parent component&#39;s Javascript.  We need to hook into one of the lifecycle callbacks in order to attach our handler&lt;/p&gt;
&lt;p&gt;You can see the &lt;a href=&quot;https://developer.salesforce.com/docs/component-library/documentation/lwc/lwc.create_lifecycle_hooks&quot;&gt;docs for those functions here.&lt;/a&gt;&lt;p&gt;

&lt;p&gt;From there we find:
&lt;ul&gt;
  &lt;li&gt;We can&#39;t use the constructor, as the component hasn&#39;t been added to the DOM yet.&lt;/li&gt;
  &lt;li&gt;We can&#39;t use the connectedCallback, as the component&#39;s children haven&#39;t been rendered yet.&lt;/li&gt;
  &lt;li&gt;We &lt;b&gt;can&lt;/b&gt; use the renderedCallback, but this gets called multiple times - whenever any reactive properties change, so we need to protect against multiple adds.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, maybe we can do this:&lt;/p&gt;

&lt;div class=&quot;aside&quot;&gt;
&lt;p&gt;&lt;b&gt;Update - 22/12/2018&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;In the following section I missed an important parameter that could be passed into the &#39;bind&#39; onto the event listener registration.  This led to a slightly convoluted solution using fat arrows.  I&#39;m leaving that text here, as I think it&#39;s an interesting aside, although at the end I&#39;ll present a simpler solution.&lt;/p&gt;
&lt;/div&gt;

&lt;pre style=&quot;brush:js&quot;&gt;
    allocatedEventListeners = false;

    renderedCallback() {
        if ( ! this.allocatedEventListeners ) {
            this.template.querySelector(&#39;.child&#39;).addEventListener( this.handleOnValueChanged.bind() );
            this.allocatedEventListeners = true;
        }
    }
&lt;/pre&gt;

&lt;p&gt;That is a bit clunky, but it looks like it should work.  We &#39;bind&#39; the &#39;handleOnValueChanged&#39; function to the event listener.&lt;/p&gt;

&lt;p&gt;Unfortunately, it doesn&#39;t.  Because of a fundamental capability of Javascript - it appears that the event handler doesn’t have access to ‘this’.  And if you’re not an experienced Javascript developer then that’s when things start to get a bit crazy (actually, even if you ARE an experienced Javascript developer, I suspect it STILL gets a little messed up).&lt;/p&gt;

&lt;p&gt;Basically, &#39;this&#39; isn’t guaranteed to be what you think it is.  If you write code that behaves in a procedural way, then it will generally be the object in which the method is defined.  But as soon as you add in callbacks, Promises and asynchronous behaviour, it isn&#39;t guaranteed to be.&lt;/p&gt;

&lt;p&gt;&#39;this&#39; &lt;i&gt;can&lt;/i&gt; be simply the context in which the function runs, rather than the object or class in which the function is defined.  This is an incredibly powerful aspect of Javascript that is very difficult to get to grips with unless you’re used to seeing it.&lt;/p&gt;

&lt;p&gt;In Lightning Components you can see the effect of this in code such as Apex callouts in helpers where you end up with:&lt;/p&gt;

&lt;pre style=&quot;brush:js&quot;&gt;
    let self = this;
&lt;/pre&gt;

&lt;p&gt;In our particular case, you could use an alternative - the fat arrow notation for defining functions.&lt;/p&gt;

&lt;pre style=&quot;brush:js&quot;&gt;
 event =&gt; { this.handleOnValueChanged( event ) }
&lt;/pre&gt;

&lt;p&gt;Which is *would* transpile to (or is synonymous with) this:&lt;/p&gt;

&lt;pre style=&quot;brush:js&quot;&gt;
    function handleEvent(event) {
        var _this = this;
        ( function (event) { _this.handleOnValueChanged(event); });
    }
&lt;/pre&gt;

&lt;p&gt;Look familiar?&lt;/p&gt;

&lt;p&gt;The resulting code for adding the event handler could end up like this:&lt;/p&gt;
&lt;pre style=&quot;brush:js&quot;&gt;
    allocatedEventListeners = false;

    renderedCallback() {
        if ( ! this.allocatedEventListeners ) {
            this.template.querySelector(&#39;.child&#39;)
                          .addEventListener( &#39;valuechanged&#39;,
                                            ( ( event ) =&gt; { this.handleOnValueChanged( event ) } ).bind() );
            this.allocatedEventListeners = true;
        }
    }
&lt;/pre&gt;

&lt;p&gt;In the end, this would work.  But no-one would suggest it was elegant.   And in order to get it working we had to brush up against some advanced behaviour of &#39;this&#39;.  Now, I admit that people are going to have to learn how &#39;this&#39; and its binding behaves in order to write reliable Lightning Web Components - but just to add an event handler?&lt;/p&gt;


&lt;div class=&quot;aside&quot;&gt;
&lt;p&gt;&lt;b&gt;Update - 22/12/2018&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;A simpler solution than a fat arrow, would be to use &#39;bind&#39; for the purpose it was designed and pass in the context into the call.  I.E. to do the following.&lt;/p&gt;
&lt;pre style=&quot;brush:js&quot;&gt;
    allocatedEventListeners = false;

    renderedCallback() {
        if ( ! this.allocatedEventListeners ) {
            this.template.querySelector(&#39;.child&#39;).addEventListener( this.handleOnValueChanged.bind( this ) );
            this.allocatedEventListeners = true;
        }
    }
&lt;/pre&gt;
&lt;p&gt;This has the same effect as the fat-arrow function, in that is ensures that when &#39;handleOnValueChanged&#39; is executed, it is done so with the context of &#39;this&#39; being that of the component&#39;s object.&lt;/p&gt;
&lt;p&gt;However, even though this is slightly easier to read, &lt;b&gt;I still do not recommend registering event listeners using Javascript.  Use the template notation instead.&lt;/b&gt;&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;The reality is that we don&#39;t have to think about it - Salesforce have given us a very usable shorthand for it, and we should be extremely grateful for it!&lt;p&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/7786793954250386040/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/10846234/7786793954250386040' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/7786793954250386040'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/7786793954250386040'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2018/12/lightning-web-components-events-and.html' title='Lightning Web Components - Events, and listening to your children'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJKWDfU-frt3gi_ubtgJ8msvcKcp6ChPvxN_uLMCR-FqygXX-IabdD7vmbQ3RHXnwvmQRPaiXacC6CoIfaYcyh0hGdV0PQzbjFaqf_ECJjuD2JzQVdrINY6YBtjgISt_4/s113/Rob+Baillie.png'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-5512445707278248483</id><published>2018-12-19T18:37:00.000+00:00</published><updated>2018-12-20T10:03:11.907+00:00</updated><title type='text'>Lightning Web Components - Unit Testing immediate impression.</title><content type='html'>&lt;p&gt;One of the things I’m most excited about in Lightning Web Components is the ability to write Unit Tests for the components.&lt;/p&gt;

&lt;p&gt;The unit testing framework of choice is Jest, and it looks well suited.  Not least it’s the framework of choice for Facebook, and describes itself as well suited to React apps.  Why should that matter?  Well, React is a 1-way bound Javascript framework - and so is LWC.&lt;/p&gt;

&lt;p&gt;So I was looking forward to get into Unit Testing, following the documentation for testing &lt;a href=&quot;https://developer.salesforce.com/docs/component-library/documentation/lwc/lwc.unit_testing_using_wire_utility&quot;&gt;wired components&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Unfortunately, the documentation didn’t work out for me, and it looked like there’s a couple of mistakes in there:&lt;/p&gt;

&lt;p&gt;&lt;b&gt;The example ‘getRecord.json’ file isn’t valid JSON.&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;In order for the file to work, the field names need to be surrounded in double quotes.&lt;/p&gt;

&lt;p&gt;I.E. Instead of:&lt;/p&gt;

&lt;pre style=&quot;brush:js;&quot;&gt;
    // getRecord.json
    {
       fields: {
           Name: {
               value: &quot;DYNAMO X1&quot;
           }
       }
    }
&lt;/pre&gt;

&lt;p&gt;The file should be:&lt;/p&gt;

&lt;pre style=&quot;brush:js;&quot;&gt;
    // getRecord.json
    {
       &quot;fields&quot;: {
           &quot;Name&quot;: {
               &quot;value&quot;: &quot;DYNAMO X1&quot;
           }
       }
    }
&lt;/pre&gt;

&lt;p&gt;&lt;b&gt;Interrogating the ‘element’ for its state does not seem to work.&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;Instead, I found that I needed to get data from the document object.&lt;/p&gt;

&lt;p&gt;I.E. The following does not work:&lt;/p&gt;

&lt;pre style=&quot;brush:js;&quot;&gt;
    // Resolve a promise to wait for a rerender of the new content.
       return Promise.resolve().then(() =&gt; {
           const content = element.querySelector(&#39;.content&#39;);
           expect(content.textContent).toBe(&quot;Name:DYNAMO X1&quot;);
       });
&lt;/pre&gt;

&lt;p&gt;But the following does:&lt;/p&gt;
&lt;pre style=&quot;brush:js;&quot;&gt;
    // Resolve a promise to wait for a rerender of the new content.
       return Promise.resolve().then(() =&gt; {
           const content = document.body.querySelector(&#39;.content&#39;);
           expect(content.textContent).toBe(&quot;Name:DYNAMO X1&quot;);
       });
&lt;/pre&gt;

&lt;p&gt;&lt;b&gt;Mocking doesn&#39;t seem to work for Apex, only LDS&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;From the quick test I did, I &lt;i&gt;could&lt;/i&gt; get the mocking framework to work for the Lightning Data Service, once my implementation of the example was tweaked.  However, I couldn&#39;t get it to work with an imported Apex method&lt;/p&gt;

&lt;p&gt;I didn&#39;t see a reference to this being missing, though I guess I may have just missed that, and I know that the recommendation is to use LDS whenever you can.  I just worry that there&#39;s a really important use case out there - it seems natural to me that components that use custom Apex are likely to be more complex than ones that use LDS.  And with that in mind, it feels like missing Apex will be a big loss to the testing framework.&lt;/p&gt;

&lt;p&gt;Hopefully the last part is already known about, is a bit of missing documentation, or is simply that I misunderstood something.&lt;/p&gt;

&lt;p&gt;Whatever the case, I plan on doing more investigations into the Unit Testing capabilities, and will obviously blog my findings -  but I have to admit that I found the initial experience a little disappointing after the extremely polished experience up to now.&lt;/p&gt;

&lt;p&gt;I sincerely hope that it isn’t an indicator that Unit Testing is bit of an after-thought.&lt;/p&gt;

&lt;h2&gt;Update - 20/12/18&lt;/h2&gt;

&lt;p&gt;It felt like a good avenue for exploring testing would be to put together a test for the &quot;message&quot; component that I put together for &lt;a href=&quot;https://robertbaillie.blogspot.com/2018/12/lightning-web-components-api-slots-and.html&quot;&gt;the blog post on re-usable components&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Unfortunately, I stumbled on this block.  I was able to pretty quickly write a test that proved that @api properties were rendered properly in the resulting HTML, but I couldn&#39;t find a way of setting the value the slot.   Adding a textNode as a child of the built node is blocked (seemingly by LWC), with an error that appears to suggest it can be worked around.  But with no substantial documentation yet available, it feels like I&#39;m just shooting in the dark&lt;/p&gt;

&lt;p&gt;When I couple that with the fact that the &lt;a href=&quot;https://github.com/trailheadapps/ebikes-lwc&quot;&gt;git repo for the ebikes app&lt;/a&gt; seems to only contain one set of tests &lt;a href=&quot;https://github.com/trailheadapps/ebikes-lwc/blob/master/force-app/main/default/lwc/productFilter/__tests__/productFilter.test.js&quot;&gt;for the productFilter component&lt;/a&gt;, I&#39;m stating to take the hint that unit testing hasn&#39;t been explored by the team fully yet.&lt;/p&gt;

&lt;p&gt;I think it&#39;s entirely understandable, and I still hope that this area will be fleshed out significantly in the coming weeks, but the first impression remains - not yet&lt;/p&gt;
</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/5512445707278248483/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/10846234/5512445707278248483' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/5512445707278248483'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/5512445707278248483'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2018/12/lightning-web-components-unit-testing.html' title='Lightning Web Components - Unit Testing immediate impression.'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJKWDfU-frt3gi_ubtgJ8msvcKcp6ChPvxN_uLMCR-FqygXX-IabdD7vmbQ3RHXnwvmQRPaiXacC6CoIfaYcyh0hGdV0PQzbjFaqf_ECJjuD2JzQVdrINY6YBtjgISt_4/s113/Rob+Baillie.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-3973945069409550593</id><published>2018-12-19T11:49:00.000+00:00</published><updated>2018-12-19T11:49:01.961+00:00</updated><title type='text'>Lightning Web Components - @api, slots and getters</title><content type='html'>&lt;p&gt;I&#39;ve blogged about a few of the behaviours of Lightning Web Components, but the proof is really in building useful bits.  What happens when you actually try to make a re-usable component?&lt;/p&gt;

&lt;p&gt;For our example, we&#39;ll rebuild &#39;ui:message&#39;.  A now (seemingly) defunct base component that would render a message in a box that is coloured based on the &#39;severity&#39; of the message being shown.  In the original it could be set to &#39;closable&#39;, although we&#39;re going to ignore that and focus on just the rendering of it.&lt;p&gt;

&lt;p&gt;In a Lightning component we would use it like this:&lt;/p&gt;
&lt;h3&gt;Original usage - Lightning Component&lt;/h3&gt;
&lt;pre style=&quot;brush:html;&quot;&gt;
    &amp;lt;ui:message title=&quot;Error&quot; severity=&quot;error&quot; &gt;{!v.errorMessages}&amp;lt;/ui:message&gt;
&lt;/pre&gt;

&lt;p&gt;Ideally, the version we will create, would be used like this:&lt;/p&gt;
&lt;h3&gt;Desired usage&lt;/h3&gt;
&lt;pre style=&quot;brush:html;&quot;&gt;
    &amp;lt;c-message title=&quot;Error&quot; severity=&quot;error&quot; &gt;{errorMessages}&amp;lt;/c-message&gt;
&lt;/pre&gt;

&lt;p&gt;Looks pretty straightforward, and actually - it is.  Just as long as we know about a few simple concepts.&lt;/p&gt;
&lt;p&gt;Before we go into them, let&#39;s see what a working example could look like:&lt;/p&gt;

&lt;h3&gt;Javascript component&lt;/h3&gt;
&lt;pre style=&quot;brush:js;&quot;&gt;
    import { LightningElement, api } from &#39;lwc&#39;;

    export default class Message extends LightningElement {

        @api title;
        @api severity;

        get classes() {
            return this.severity + &#39; uiMessage&#39;;
        }
    }
&lt;/pre&gt;

&lt;h3&gt;HTML Template&lt;/h3&gt;
&lt;pre style=&quot;brush:html;&quot;&gt;
    &amp;lt;template&gt;
        &amp;lt;div class={classes} role=&quot;alert&quot; &gt;
            &amp;lt;div class=&quot;uiBlock&quot; &gt;
                &amp;lt;div class=&quot;bBody&quot; &gt;
                    &amp;lt;h4&gt;{title}&amp;lt;/h4&gt;&amp;lt;slot&gt;&amp;lt;/slot&gt;
                &amp;lt;/div&gt;
            &amp;lt;/div&gt;
        &amp;lt;/div&gt;
    &amp;lt;/template&gt;
&lt;/pre&gt;

&lt;p&gt;OK then, let&#39;s pick a few of these bits apart, and hopefully we&#39;ll explain a few little behaviours along the way.&lt;/p&gt;

&lt;p&gt;First up, let&#39;s take a look at the &#39;@api&#39; declarations.&lt;/p&gt;

&lt;h2&gt;@api&lt;/h2&gt;

&lt;p&gt;The @api property lines are pretty simple to understand - they define that &#39;title&#39; and &#39;severity&#39; are publicly available properties of the component.  In the context of Lightning Web Components, public and private mean &#39;available outside of the component, and invisible to the outside of the component&#39;.  It&#39;s tempting to think that this is to do with the scope of the Javascript, but it&#39;s not.&lt;/p&gt;

&lt;p&gt;That is, every property of the Javascript component is available to be referenced in the HTML template - whether it is &#39;public&#39; or &#39;private&#39;.  One way of thinking about it is that the HTML template forms part of the component, and so it can see private properties.&lt;/p&gt;

&lt;p&gt;Another (probably more accurate) way of thinking about it is that the template is processed by the Javascript component (that code it&#39;s immediately obvious, but it&#39;s almost certainly in LightningComponent - which this class extends), and the Javascript can see its own properties, so the private ones are available.&lt;/p&gt;

&lt;p&gt;However, other components (like ones that include this in their templates) can &lt;b&gt;only&lt;/b&gt; see public properties.  @api is how you make them public.  Doing so means that they are available as attributes on the tag you use to include the component (hence &amp;lt;c-message title=&quot;Error&quot;... is possible)&lt;/p&gt;

&lt;p&gt;Not only that, but every @api decorated property is also &#39;reactive&#39;.  That is, whenever its value changes the component is re-rendered.  &lt;a href=&quot;https://developer.salesforce.com/docs/component-library/documentation/lwc/lwc.js_props_reactive&quot;&gt;The documentation is pretty clear on that point&lt;/a&gt; - and is presented as a fundamental property of a public property:&lt;/p&gt;

&lt;div class=&quot;aside&quot;&gt;
&lt;b&gt;Public Properties&lt;/b&gt;
&lt;br/&gt;
&lt;p&gt;To expose a public property, decorate it with @api. Public properties define the API for a component. An owner component that uses the component in its markup can access the component’s public properties. Public properties are reactive. If the value of a reactive property changes, the component’s template rerenders any content that references the property.&lt;/p&gt;
&lt;/div&gt;
&lt;br/&gt;

&lt;p&gt;Why would a public property be reactive?&lt;/p&gt;

&lt;p&gt;Put simply, if we change the value of one of those properties in a parent component, we want the component to re-render - and it&#39;s pretty much guaranteed that we ALWAYS want the component to re-render.&lt;/p&gt;

&lt;p&gt;For example, we may do the following:&lt;/p&gt;

&lt;pre style=&quot;brush:html;&quot;&gt;
    &amp;lt;c-message title=&quot;{title}&quot; severity=&quot;{severity}&quot; &gt;{messages}&amp;lt;/c-message&gt;
&lt;/pre&gt;

&lt;p&gt;When the value of &#39;title&#39; or &#39;severity&#39; changes, we would &lt;b&gt;always&lt;/b&gt; want the message box to re-render to show our new values.  And so the framework takes care of that and makes EVERY public property of the component reactive&lt;/p&gt;

&lt;p&gt;So that takes care of the attributes we need to pass in, what about the content?&lt;/p&gt;

&lt;h2&gt;Slots&lt;/h2&gt;

&lt;p&gt;Lightning Components had facets.  And they weren&#39;t intuitive.  I mean they weren&#39;t complex, but they weren&#39;t in keeping with HTML - they always felt unnatural - especially in the simplest of cases.&lt;/p&gt;

&lt;p&gt;Lightning Web Components fixes that, with slots.  And in the simple case they are trivial.  The &lt;a href=&quot;https://developer.salesforce.com/docs/component-library/documentation/lwc/lwc.create_components_slots&quot;&gt;documentation isn&#39;t long, and doesn&#39;t need to be.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All we need to do, in this simple case, is add &amp;lt;slot&gt;&amp;lt;/slot&gt; into our component, and the body of any tag that instantiates the component will be rendered in that slot.&lt;/p&gt;

&lt;p&gt;Now something that&#39;s missing from the documentation, which is a fairly obvious behaviour once you see it in action, is that slots are &lt;i&gt;effectively&lt;/i&gt; reactive.&lt;/p&gt;

&lt;p&gt;That is, if you change the content of the tag, that content is immediately reflected in the component&#39;s rendered output.&lt;/p&gt;

&lt;p&gt;So, in our example:&lt;/p&gt;
&lt;pre style=&quot;brush:html;&quot;&gt;
    &amp;lt;c-message title=&quot;Error&quot; severity=&quot;error&quot; &gt;{errorMessages}&amp;lt;/c-message&gt;
&lt;/pre&gt;
&lt;p&gt;Whenever the value of &#39;errorMessages&#39; changes, the slot inside the &#39;message&#39; component is re-rendered to include the new content.&lt;/p&gt;
&lt;p&gt;I admit, I had assumed that this would be the case, but I didn&#39;t immediately realise that it &lt;b&gt;was&lt;/b&gt; an assumption.  So I thought it was worth calling out&lt;/p&gt;

&lt;h2&gt;Getters&lt;/h2&gt;

&lt;p&gt;The final part of the example that I want to explain is the use of the &#39;getter&#39;:&lt;/p&gt;

&lt;pre style=&quot;brush:js;&quot;&gt;
    get classes() {
        return this.severity + &#39; uiMessage&#39;;
    }
&lt;/pre&gt;

&lt;p&gt;What we&#39;re doing here is building a list of CSS classes for a node in the component that includes one of the passed in attributes plus a standard class that must be applied&lt;/p&gt;
&lt;p&gt;The use of the getter illustrates an important difference between the behaviour of the templates in Lightning Components (LC) and Lightning Web Components (LWC), as well a reminder of the behaviour of properties.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;That is, in LC we could have done the following in our template:&lt;/b&gt;&lt;/p&gt;

&lt;pre style=&quot;brush:html;&quot;&gt;
    &amp;lt;div class=&quot;{!v.severity + &#39; uiMessage&#39;}&quot; role=&quot;alert&quot; &gt;
&lt;/pre&gt;

&lt;p&gt;In LC, our replacements could include expressions, so we could build up strings in the template.  In LWC, we can&#39;t do this, we can only reference properties or getters.&lt;/p&gt;

&lt;p&gt;Not only that, but we can&#39;t build up the strings in the attribute assignment.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;I.E. We can&#39;t do this:&lt;/b&gt;&lt;/p&gt;

&lt;pre style=&quot;brush:html;&quot;&gt;
    &amp;lt;div class=&quot;{severity} uiMessage&quot; role=&quot;alert&quot; &gt;
&lt;/pre&gt;

&lt;p&gt;In LWC we don&#39;t assign properties to attributes in this way, the framework takes care of the wrapping in double quotes, escaping the strings, and other such things, so we can &lt;b&gt;only&lt;/b&gt; assign the property, and that&#39;s it.&lt;p&gt;
&lt;p&gt;&lt;b&gt;I.E. This is what is allowed:&lt;/b&gt;&lt;/p&gt;

&lt;pre style=&quot;brush:html;&quot;&gt;
    &amp;lt;div class={severity} role=&quot;alert&quot; &gt;
&lt;/pre&gt;

&lt;p&gt;So, if we want to assign more than just the value of &#39;severity&#39; to the class attribute, &lt;b&gt;we need to build that string up outside of the template.&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;Your first reaction might be - OK, we can create a trackable property to store it, right?&lt;/p&gt;

&lt;pre style=&quot;brush:js;&quot;&gt;
    @track classes = this.severity + &#39; uiMessage&#39;;
&lt;/pre&gt;

&lt;p&gt;But this doesn&#39;t work.  You&#39;ll end up with the classes property defined as &#39;undefined uiMessage&#39;, and it won&#39;t change.  Why is that?&lt;/p&gt;

&lt;p&gt;Well, it&#39;s tempting to think that &#39;track&#39; and &#39;api&#39; mean that Javascript will re-run when things change, but that&#39;s not what they do - nor what the documentation says they&#39;ll do&lt;/p&gt;

&lt;p&gt;Rather, if a property is reactive it means that &lt;b&gt;the component will be re-rendered when the property changes&lt;/b&gt;.  That says nothing about running Javascript.&lt;/p&gt;

&lt;p&gt;So when we look at the above, what happens is the property &#39;classes&#39; is set when the Javascript object is constructed.  At this point the property &#39;severity&#39; is undefined.  When the &#39;severity&#39; is updated via the attribute, the component is re-rendered and the &#39;classes&#39; property is re-injected into the template, but the Javascript that &lt;b&gt;sets&lt;/b&gt; the classes property is &lt;b&gt;not&lt;/b&gt; re-run - that is only executed when the object is instantiated.&lt;/p&gt;

&lt;p&gt;So, instead of setting the &#39;classes&#39; property directly, we set up a getter for it:&lt;/p&gt;

&lt;h3&gt;Javascript component&lt;/h3&gt;
&lt;pre style=&quot;brush:js;&quot;&gt;
    get classes() {
        return this.severity + &#39; uiMessage&#39;;
    }
&lt;/pre&gt;

&lt;p&gt;Now, when the &#39;severity&#39; property changes, the &#39;classes&#39; property is re-injected.  In order to get the value for &#39;classes&#39;, the getter is executed - this is the only way the property can be retrieved.  In doing so, the string concatenation is re-evaluated and the new value is retrieved.&lt;/p&gt;

&lt;h3&gt;Summary&lt;/h3&gt;

&lt;p&gt;None of the concepts here are particularly difficult, or really that earth shattering, but building even the simplest of re-usable components starts to shed some light on what the parts do any why.&lt;/p&gt;

&lt;p&gt;The framework has been very thoughtfully put together, and some of the techniques will be different to what people are used to, having cut their Javascript teeth with Lightning Components, but the changes are for very good reasons.  An example like this really shows how those changes make for simple components.&lt;/p&gt;

</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/3973945069409550593/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/10846234/3973945069409550593' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/3973945069409550593'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/3973945069409550593'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2018/12/lightning-web-components-api-slots-and.html' title='Lightning Web Components - @api, slots and getters'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJKWDfU-frt3gi_ubtgJ8msvcKcp6ChPvxN_uLMCR-FqygXX-IabdD7vmbQ3RHXnwvmQRPaiXacC6CoIfaYcyh0hGdV0PQzbjFaqf_ECJjuD2JzQVdrINY6YBtjgISt_4/s113/Rob+Baillie.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-5980210743227993986</id><published>2018-12-18T12:31:00.001+00:00</published><updated>2018-12-19T14:32:52.212+00:00</updated><title type='text'>Lightning Web Components - The subtleties of Tracking and Wiring</title><content type='html'>&lt;p&gt;Following on from &lt;a href=&quot;https://robertbaillie.blogspot.com/2018/12/lightning-web-components-art-of-tracking.html&quot;&gt;yesterday&#39;s investigations into the behaviour of &#39;@track&#39;&lt;/a&gt;, and its effect on untracked properties, I figured I should expand my remit to &#39;@wire&#39;.&lt;/p&gt;

&lt;p&gt;My main reason for that was the statement in &lt;a href=&quot;https://developer.salesforce.com/docs/component-library/documentation/lwc/lwc.data_wire_service_about&quot;&gt;the documentation:&lt;/a&gt;&lt;/p&gt;

&lt;div class=&quot;aside&quot;&gt;In the wire adapter’s configuration object, prefix a property with $ to reference a property of the component instance. The $ prefix tells the wire service to treat it as a property of the class and evaluate it as this.propertyName. The property is reactive. If the property’s value changes, new data is provisioned and the component rerenders.&lt;/div&gt;

&lt;p&gt;This comment relates to code along the lines of:&lt;p&gt;

&lt;pre class=&quot;brush: js&quot;&gt;
    @api recordId;

    @wire(getRecord, { recordId: &#39;$recordId&#39;, fields })
    contact;
&lt;/pre&gt;

&lt;p&gt;The bit that really piqued my interest was &#39;The property is reactive&#39;.&lt;/p&gt;

&lt;p&gt;In all the examples, it looked like the property being referenced was always tracked or set as an api field (the latter implying the former)&lt;/p&gt;
&lt;p&gt;That&#39;s the case in the above example - the property passed as a parameter &#39;recordId&#39; as referenced by &#39;$recordId&#39; is defined as an api field, and is therefore tracked.&lt;/p&gt;

&lt;p&gt;There&#39;s also that recurring point &#39;the component rerenders&#39;, which we saw in my previous post.  Recalling that, it essentially meant that a change to a tracked property caused all untracked properties in the same component (and I am assured, but yet to prove, child components) to be re-rendered&lt;/p&gt;

&lt;p&gt;So, what is the actual behaviour in this case?  Are the implications the same?&lt;/p&gt;

&lt;div class=&quot;aside&quot;&gt;You can code that illustrates the below examples, as well as the points from yesterday&#39;s post in &lt;a href=&quot;https://bitbucket.org/robbaillie_mp/lwc-experiments&quot;&gt;this bitbucket repository&lt;/a&gt; if you want to explore the behaviours further.  I suggest you take a look.&lt;/div&gt;
&lt;br/&gt;

&lt;h2&gt;Tracked property used as a parameter to a wired property&lt;/h2&gt;

&lt;p&gt;The natural first example is much like the one exampled above.  We have an Apex method that takes a parameter.  We wire that parameter to a javascript property, and reference a single parameter.&lt;/p&gt;

&lt;h3&gt;Javascript component&lt;/h3&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
    @track searchString;

    @wire(searchContacts, { searchString: &#39;$searchString&#39; })
    contacts;
&lt;/pre&gt;

&lt;p&gt;When the searchString property is updated:
&lt;ul&gt;
  &lt;li&gt;The searchString&#39;s change causes the wired function to be re-evaluated.&lt;/li&gt;
  &lt;li&gt;Therefore the value of &#39;contacts&#39; changes.&lt;/li&gt;
  &lt;li&gt;The component treats contacts as if it was tracked and causes the component to be re-rendered.&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;OK.  That&#39;s pretty decent, and what the documentation says will happen.  No surprises there!&lt;/p&gt;
&lt;p&gt;But then I got to thinking: &lt;b&gt;So, what happens if the property isn&#39;t tracked?&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;My first guess was that maybe the Apex method wouldn&#39;t get re-executed, but I wasn&#39;t entirely convinced - after all, the documentation only really makes a big deal about rendering.  So I changed the code and tried again...&lt;/p&gt;

&lt;h3&gt;Javascript component&lt;/h3&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
    searchString;

    @wire(searchContacts, { searchString: &#39;$searchString&#39; })
    contacts;
&lt;/pre&gt;

&lt;p&gt;This time, when the searchString property is updated:
&lt;ul&gt;
  &lt;li&gt;The searchString&#39;s change causes the wired function to be re-evaluated.&lt;/li&gt;
  &lt;li&gt;Therefore the value of &#39;contacts&#39; changes.&lt;/li&gt;
  &lt;li&gt;The component treats contacts as if it was tracked and causes the component to be re-rendered.&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;Erm.  OK.  It&#39;s the same.  Hmmm....&lt;/p&gt;

&lt;p&gt;Looking at what&#39;s actually going on, that &lt;b&gt;does&lt;/b&gt; make sense.  It&#39;s the &#39;$searchString&#39; reference that tells the framework that searchString is reactive, as respect to the wiring, so it&#39;s &lt;b&gt;that&lt;/b&gt; causing the Apex to re-execute.  And once the method is re-evaluated, the value of &#39;contacts&#39; changes, and that causes the component to re-render.&lt;/p&gt;

&lt;p&gt;That got me to thinking - so what does the &#39;@track&#39; do against the searchString in the original example.  Experience tells me that these things generally &lt;b&gt;will&lt;/b&gt; have an effect.&lt;/p&gt;

&lt;p&gt;So I added something else to my example...&lt;/p&gt;

&lt;pre class=&quot;brush: js&quot;&gt;
    renderedCallback() {
        console.log( &#39;renderedCallback was called&#39; );
    }
&lt;/pre&gt;

&lt;p&gt;This hooks into the component&#39;s lifecycle, as &lt;a href=&quot;https://developer.salesforce.com/docs/component-library/documentation/lwc/lwc.create_lifecycle_hooks_intro&quot;&gt;described here&lt;/a&gt;, and will tell us when the component gets re-rendered.&lt;/p&gt;

&lt;p&gt;It turns out that my initial understanding of the first example was slightly wrong, though not in a way that would generally have much of an impact.&lt;/p&gt;
&lt;p&gt;That is, if the parameter is tracked, you end up with the following:
&lt;ul&gt;
  &lt;li&gt;The searchString&#39;s change causes:
    &lt;ul&gt;
        &lt;li&gt;&lt;b&gt;The component to be re-rendered&lt;/b&gt; (since it is tracked).&lt;/li&gt;
        &lt;li&gt;The wired function to be re-evaluated (since it is referenced as a &#39;$&#39; parameter).&lt;/li&gt;
    &lt;/ul&gt;
  &lt;li&gt;The execution of the wired function causes the value of &#39;contacts&#39; to change.&lt;/li&gt;
  &lt;li&gt;The component treats contacts as if it was tracked and causes the &lt;b&gt;component to be re-rendered&lt;/b&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/p&gt;

&lt;p&gt;&lt;b&gt;The result is that the component is re-rendered twice!&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;And sure enough, if you take the tracking off the searchString parameter, the component is only re-rendered once.&lt;/p&gt;

&lt;p&gt;So, this does reinforce another point that the &lt;a href=&quot;https://developer.salesforce.com/docs/component-library/documentation/lwc/lwc.js_props_private_reactive&quot;&gt;documentation makes here&lt;/a&gt;:&lt;p&gt;

&lt;div class=&quot;aside&quot;&gt;&lt;b&gt;Don’t overuse @track. Track a property only if you need the component to rerender when the property’s value changes.&lt;/b&gt;&lt;/div&gt;

&lt;p&gt;Personally, I think I&#39;d call out this particular behaviour and remind people - you don&#39;t need to &#39;track&#39; a property in order for the wired method to re-execute - but maybe it&#39;s just my lack of attention that missed that little point.&lt;/p&gt;

&lt;p&gt;And I&#39;d also state that you should only track a property that is being used as a reactive wired parameter if you need the property to be re-rendered before the Apex you are calling returns with its result.&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/5980210743227993986/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/10846234/5980210743227993986' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/5980210743227993986'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/5980210743227993986'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2018/12/lightning-web-components-subtleties-of.html' title='Lightning Web Components - The subtleties of Tracking and Wiring'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJKWDfU-frt3gi_ubtgJ8msvcKcp6ChPvxN_uLMCR-FqygXX-IabdD7vmbQ3RHXnwvmQRPaiXacC6CoIfaYcyh0hGdV0PQzbjFaqf_ECJjuD2JzQVdrINY6YBtjgISt_4/s113/Rob+Baillie.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-2436462718664873889</id><published>2018-12-17T14:15:00.003+00:00</published><updated>2018-12-17T16:51:37.393+00:00</updated><title type='text'>Lightning Web Components - the art of Tracking</title><content type='html'>&lt;p&gt;On Friday I wrote an excited &lt;a href=&quot;http://robertbaillie.blogspot.com/2018/12/lightning-web-components-dawn-of.html&quot;&gt;blog post on the introduction of Lightning Web Components.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One of the things I mentioned was that I didn&#39;t quite get how &#39;@track&#39; was supposed to work&lt;/p&gt;

&lt;p&gt;Since then, I&#39;ve found the documentation (add /docs/component-library/documentation/lwc/lwc.get_started_introduction to the end of your Salesforce URL), read it, got confused by my memory, wrote a test-rig and looked at it all in depth.&lt;/p&gt;

&lt;p&gt;On the surface, it&#39;s simple, but there are some complications, which were the cause of my confusion...&lt;/p&gt;

&lt;p&gt;In the documentation it says the following:&lt;/p&gt;

&lt;div class=&quot;aside&quot;&gt;
Both @track and @api mark a property as reactive. If the property’s value changes, the component re-renders.
&lt;/div&gt;

&lt;p&gt;OK, nice and simple.  So what does that mean?&lt;/p&gt;


&lt;h2&gt;A single untracked property&lt;/h2&gt;

&lt;p&gt;Testing with a simple component that contains a single property that is &lt;b&gt;not&lt;/b&gt; tracked I found the following behaviour:&lt;/p&gt;

&lt;h3&gt;Javascript component&lt;/h3&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
    export default class TrackExperiments extends LightningElement {
        primitiveUntracked;

        handlePrimitiveUntrackChanged( event ) {
            this.primitiveUntracked = event.target.value;
        }
    }
&lt;/pre&gt;

&lt;h3&gt;Template&lt;/h3&gt;
&lt;pre class=&quot;brush: html&quot;&gt;
    &amp;lt;p&gt;The value is: {primitiveUntracked}&amp;lt;/p&gt;
    &amp;lt;lightning-input label=&quot;Input without an on-change handler&quot;
                        type=&quot;Text&quot;
                        value={primitiveUntracked}&gt;&amp;lt;/lightning-input&gt;
    &amp;lt;lightning-input label=&quot;Input with an on-change handler&quot;
                        type=&quot;Text&quot;
                        value={primitiveUntracked}
                        onchange={handlePrimitiveUntrackChanged}&gt;&amp;lt;/lightning-input&gt;
&lt;/pre&gt;

&lt;ul&gt;
  &lt;li&gt;Changing the value defined in either of the inputs will not result in the rendered value changing.&lt;/li&gt;
  &lt;li&gt;Changing the value defined in the input &lt;b&gt;without&lt;/b&gt; the onchange handler will not change the Javascript property.&lt;/li&gt;
  &lt;li&gt;Changing the value defined in the input that &lt;b&gt;does&lt;/b&gt; have an onchange handler will update the Javascript property.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, fairly straightforward, and maybe in-line with what you would expect:&lt;p&gt;
&lt;ul&gt;
  &lt;li&gt;Untracked properties are not re-rendered in the UI when they change&lt;/li&gt;
  &lt;li&gt;Untracked properties that are bound to inputs are not automatically updated when the input changes unless there is an onchange handler defined that updates the Javascript property&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;A single untracked property&lt;/h2&gt;

&lt;p&gt;Testing with a simple component that contains a single property that &lt;b&gt;is&lt;/b&gt; tracked, I found the following behaviour:&lt;/p&gt;

&lt;h3&gt;Javascript component&lt;/h3&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
    export default class TrackExperiments extends LightningElement {
        @track primitiveTracked;

        handlePrimitiveTrackChanged( event ) {
            this.primitiveTracked = event.target.value;
        }
    }
&lt;/pre&gt;

&lt;h3&gt;Template&lt;/h3&gt;
&lt;pre class=&quot;brush: html&quot;&gt;
    &amp;lt;p&gt;The value is: {primitiveTracked}&amp;lt;/p&gt;
    &amp;lt;lightning-input label=&quot;Input without an on-change handler&quot;
                        type=&quot;Text&quot;
                        value={primitiveTracked}&gt;&amp;lt;/lightning-input&gt;
    &amp;lt;lightning-input label=&quot;Input with an on-change handler&quot;
                        type=&quot;Text&quot; value={primitiveTracked}
                        onchange={handlePrimitiveTrackChanged}&gt;&amp;lt;/lightning-input&gt;
&lt;/pre&gt;

&lt;ul&gt;
&lt;li&gt;Whenever the value of the Javascript property changes, it is reflected on the outputted page.&lt;/li&gt;
&lt;li&gt;Changing the value defined in the input &lt;b&gt;without&lt;/b&gt; the onchange handler will not change the Javascript property, and so the rendered property does not change.&lt;/li&gt;
&lt;li&gt;Changing the value defined in the input that &lt;b&gt;does&lt;/b&gt; have an onchange handler will update the Javascript property, and both the rendered property &lt;b&gt;and&lt;/b&gt; the value in the other input is updated.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;OK, in my opinion, slightly odd - I was expecting the input binding to be two-way - that&#39;s not the case:
&lt;p&gt;&lt;b&gt;Lightning Web Components contain 1-way binding.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;Still, it may have surprised me, but it&#39;s easy to remember, and no big deal once you know that&#39;s the behaviour.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Tracked properties are re-rendered in the UI when they change.&lt;/li&gt;
  &lt;li&gt;Tracked properties that are bound to inputs are not automatically updated when the input changes unless there is an onchange handler defined that updates the Javascript property.&lt;/li&gt;
  &lt;li&gt;Inputs that are bound to tracked properties are automatically updated when the underlying property is updated.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;A tracked property and an untracked property on the same page&lt;/h2&gt;

&lt;h3&gt;Javascript component&lt;/h3&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
    export default class TrackExperiments extends LightningElement {
        @track primitiveTracked;
        primitiveUntracked;

        handlePrimitiveTrackChanged( event ) {
            this.primitiveTracked = event.target.value;
        }

        handlePrimitiveUntrackChanged( event ) {
            this.primitiveUntracked = event.target.value;
        }
}
&lt;/pre&gt;

&lt;h3&gt;Template&lt;/h3&gt;
&lt;pre class=&quot;brush: html&quot;&gt;
    &amp;lt;p&gt;The value is: {primitiveTracked}&amp;lt;/p&gt;
    &amp;lt;lightning-input label=&quot;Input without an on-change handler&quot;
                        type=&quot;Text&quot;
                        value={primitiveTracked}&gt;&amp;lt;/lightning-input&gt;
    &amp;lt;lightning-input label=&quot;Input with an on-change handler&quot;
                        type=&quot;Text&quot; value={primitiveTracked}
                        onchange={handlePrimitiveTrackChanged}&gt;&amp;lt;/lightning-input&gt;

    &amp;lt;p&gt;The value is: {primitiveUntracked}&amp;lt;/p&gt;
    &amp;lt;lightning-input label=&quot;Input without an on-change handler&quot;
                        type=&quot;Text&quot;
                        value={primitiveUntracked}&gt;&amp;lt;/lightning-input&gt;
    &amp;lt;lightning-input label=&quot;Input with an on-change handler&quot;
                        type=&quot;Text&quot;
                        value={primitiveUntracked}
                        onchange={handlePrimitiveUntrackChanged}&gt;&amp;lt;/lightning-input&gt;
&lt;/pre&gt;

&lt;p&gt;Now things start to get a little surprising.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Changing the inputs related to the tracked property works as described above, as if it was the only property on page.&lt;/li&gt;
&lt;li&gt;Then, changing the inputs related to the untracked property as described above.&lt;/li&gt;
&lt;li&gt;However, changing the tracked property inputs &lt;b&gt;after&lt;/b&gt; changing the untracked input, causes both the tracked &lt;b&gt;and&lt;/b&gt; untracked properties to be re-rendered.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The last point &lt;b&gt;really&lt;/b&gt; took me by surprise - I was expecting that only the tracked property would be updated.  This led me to go back to the documentation again and re-read it.&lt;/p&gt;

&lt;p&gt;That is (bold added for emphasis):&lt;/p&gt;
&lt;div class=&quot;aside&quot;&gt;
Both @track and @api mark a property as reactive. If the property’s value changes, &lt;b&gt;the component rerenders.&lt;/b&gt;
&lt;/div&gt;

&lt;p&gt;Yep, I think I&#39;m reading that right, and it looks like it&#39;s reflected in the behaviour I see (without delving &lt;b&gt;too&lt;/b&gt; deep into it).  When you change the value of a tracked property, &lt;b&gt;the whole component re-renders&lt;/b&gt;.  This means that any &lt;b&gt;untracked&lt;/b&gt; properties will also be re-rendered.

&lt;p&gt;I&#39;ve put together a more complete illustration of that, &lt;a href=&quot;https://bitbucket.org/robbaillie_mp/lwc-experiments/src/master/&quot;&gt;which you can find in this git repo.&lt;/a&gt;&lt;/p&gt;
</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/2436462718664873889/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/10846234/2436462718664873889' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/2436462718664873889'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/2436462718664873889'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2018/12/lightning-web-components-art-of-tracking.html' title='Lightning Web Components - the art of Tracking'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJKWDfU-frt3gi_ubtgJ8msvcKcp6ChPvxN_uLMCR-FqygXX-IabdD7vmbQ3RHXnwvmQRPaiXacC6CoIfaYcyh0hGdV0PQzbjFaqf_ECJjuD2JzQVdrINY6YBtjgISt_4/s113/Rob+Baillie.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-4460394515160592513</id><published>2018-12-14T14:04:00.002+00:00</published><updated>2018-12-17T15:48:58.390+00:00</updated><title type='text'>Lightning Web Components - the dawn of (another) new era</title><content type='html'>&lt;div class=&quot;aside&quot;&gt;Note that this post was put together before the documentation for Lightning Web Components went live at &lt;a href=&quot;https://developer.salesforce.com/docs/component-library/documentation/lwc&quot;&gt;developer.salesforce&lt;/a&gt;.  That site should be your first port for any explanation of a concept in LWC!&lt;/div&gt;

&lt;p&gt;Salesforce have a new technology.  Lightning Components look like they’re on the way out, and are being replaced with a new technology ‘Lightning Web Components’.&lt;/p&gt;

&lt;p&gt;The reasons behind that, and the main principles behind its design are covered very nicely &lt;a href=&quot;https://developer.salesforce.com/blogs/2018/12/introducing-lightning-web-components.html&quot;&gt;in this article on developer.salesforce.com.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From that we can then get to &lt;a href=&quot;https://github.com/trailheadapps/lwc-recipes&quot;&gt;a series of examples here.&lt;/a&gt;&lt;/p&gt;
 
&lt;p&gt;(Note: some of the code snippets used below, to illustrate points, are taken from the recipes linked above)&lt;/p&gt;

&lt;p&gt;Now I’m a big supporter of evolution, and I love to see new tools being given to developers on the Salesforce platform, so, with a couple of hours to play with it - what’s the immediate impression?&lt;/p&gt;

&lt;div class=&quot;aside&quot;&gt;This is an article on early impressions, based on reviewing and playing with the examples - I fully expect there to be misunderstandings, bad terminology, and mistakes in here - If you&#39;re OK with that, I&#39;m OK with that.  I admit, I got excited and wanted to post something as quickly as possible before my cynical side took over.  So here it is - mistakes and all.&lt;/div&gt;

&lt;br/&gt;

&lt;h2&gt;WOW.  Salesforce UI development has grown up.&lt;/h2&gt;

&lt;p&gt;Salesforce aren’t lying when they’ve said that they’re trying to bring the development toolset up to the modern standards.&lt;/p&gt;

&lt;p&gt;We get imports, what look like annotations and decorators, and there’s even mention of Promises.  Maybe there’s some legs in this…&lt;/p&gt;

&lt;p&gt;It’s easy to dismiss this as ‘Oh no, yet another change’, but the thing is - the rest of industry develops and improves its toolset - why shouldn’t Salesforce?&lt;/p&gt;

&lt;p&gt;The only way to keep the product on point IS to develop the frameworks, replace the technology, upgrade, move on.  If you don’t do that then the whole Salesforce Ecosystem starts to stagnate.&lt;/p&gt;

&lt;p&gt;Or to put it another way - in every other part of the developer community, learning from what was built yesterday and evolving is seen as a necessity.  It’s good to see Salesforce trying to keep up.&lt;/p&gt;

&lt;p&gt;So what are the big things that I’ve spotted immediately?&lt;/p&gt;

&lt;h2&gt;import is supported, and that makes things clearer&lt;/h2&gt;

&lt;p&gt;Import is a massive addition to Javascript that natively allows us to define the relationships between javascript files within javascript, rather than at the HTML level.&lt;/p&gt;

&lt;p&gt;Essentially, this replaces the use of most ‘script’ tags in traditional Javascript development.&lt;/p&gt;

&lt;p&gt;For Lightning Web Components, we use this to bring in capabilities from the framework, as well as static resources.&lt;/p&gt;

&lt;p&gt;E.g. Importing modules from the Lightning Web Components framework:&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
 import { LightningElement, track } from &#39;lwc&#39;;
&lt;/pre&gt;

&lt;p&gt;Importing from Static Resources:&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
 import { loadScript } from &#39;lightning/platformResourceLoader’;
 import chartjs from &#39;@salesforce/resourceUrl/chart&#39;;
&lt;/pre&gt;

&lt;p&gt;What this has allowed Salesforce to do is to split up the framework into smaller components.  If you don’t need to access Apex from your web component, then you don’t need to import the part of the framework that enables that capability.&lt;/p&gt;

&lt;p&gt;This *should* make individual components much more lightweight and targeted - only including the capabilities that are required, when they are required.&lt;/p&gt;

&lt;h2&gt;Getting data on screen is simpler&lt;/h2&gt;

&lt;p&gt;Any javascript property is visible to the HTML template.&lt;/p&gt;

&lt;p&gt;E.g.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
    export default class WebAppComponentByMe extends LightningElement {
     contacts;
&lt;/pre&gt;

&lt;p&gt;We can then render this property in the HTML with {contacts} (none of those attributes to define and none of those pesky v dot things to forget).&lt;/p&gt;

&lt;p&gt;Much neater, much more concise.&lt;/p&gt;

&lt;h2&gt;We track properties&lt;/h2&gt;

&lt;p&gt;Looking at the examples, my assumption was that if we want to perform actions when a property is changed, we mark the property trackable using the @track decorator.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
    export default class WebAppComponentByMe extends LightningElement {
     @track contacts;
&lt;/pre&gt;

&lt;p&gt;I was thinking that, at this point, anything that references this property (on page, or in Javascript) will be notified whenever that property changes.&lt;/p&gt;

&lt;p&gt;However, at this point I can&#39;t really tell what the difference is between tracked and non-tracked properties - a mystery for another day&lt;/p&gt;

&lt;div class=&quot;aside&quot;&gt;Since writing this, &lt;a href=&quot;https://robertbaillie.blogspot.com/2018/12/lightning-web-components-art-of-tracking.html&quot;&gt;things have become a little clearer, and I&#39;ve started posting how and why here.&lt;/a&gt;&lt;/div&gt;

&lt;h2&gt;Wiring up to Apex is much simpler&lt;/h2&gt;

&lt;p&gt;One of the big criticisms of Lightning Components that I always had was the amount of code you need to write in order to call an Apex method.  OK, so you have force:recordData for a lot of situations, but there are many times when only an Apex method will do.&lt;/p&gt;

&lt;p&gt;In Web Components, this is much simpler.&lt;/p&gt;

&lt;p&gt;In order to connect to Apex, we import the ‘wire’ module, and then import functions into our javascript&lt;/p&gt;

&lt;pre class=&quot;brush: js&quot;&gt;
 import { LightningElement, wire } from &#39;lwc&#39;;
 import getContactList from &#39;@salesforce/apex/ContactController.getContactList&#39;;
&lt;/pre&gt;

&lt;p&gt;The first line imports the wire capabilities from the framework, the second then imports the Apex method as a javascript method, therefore making it available to the component.&lt;/p&gt;

&lt;p&gt;We can then connect a javascript property up to the method using the wire decorator:&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
 @wire(getContactList) contacts;
&lt;/pre&gt;

&lt;p&gt;Or wire up a javascript method:&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
 @wire(getContactList)
 wiredContacts({ error, data }) {
  if (data) {
   this.contacts = data;
  } else if (error) {
   this.error = error;
  }
 }
&lt;/pre&gt;

&lt;p&gt;When the component is initialised, the getContactList method will be executed.&lt;/p&gt;

&lt;p&gt;If the method has parameters, that’s also very simple (E.g. wiring to a property):&lt;/p&gt;

&lt;pre class=&quot;brush: js&quot;&gt;
 @wire(getContactList, { searchKey: &#39;$searchKey&#39; })
 contacts;
&lt;/pre&gt;
&lt;br/&gt;

&lt;h2&gt;Changing the value of a property causes Apex to re-execute&lt;/h2&gt;

&lt;p&gt;Having wired up a property as a parameter to an Apex bound Javascript function, any changes to that property will cause the function to be re-executed&lt;/p&gt;

&lt;p&gt;For example, if we:
&lt;pre class=&quot;brush: js&quot;&gt;
 searchKey = &#39;&#39;;

 @wire(findContacts, { searchKey: &#39;$searchKey&#39; })
 contacts;
&lt;/pre&gt;

&lt;p&gt;Whenever the searchKey property changes, the Apex method imported as ‘findContacts’ will be executed and the contacts property is updated.&lt;/p&gt;

&lt;p&gt;Thankfully, we can control when that property changes, as it looks like changing the property in the UI does not automatically fire a change the property on the Javascript object.  In order to do that, we need to change the property directly.&lt;/p&gt;

&lt;p&gt;E.g. Let’s say we extend the previous example and there’s an input that is bound to the property, and there’s an onchange event defined:&lt;/p&gt;

&lt;pre class=&quot;brush: html&quot;&gt;
 &lt;lightning-input type=&quot;search&quot; onchange={handleKeyChange} value={searchKey}&gt;&lt;/lightning-input&gt;
&lt;/pre&gt;

&lt;p&gt;And the handler does the following:&lt;/p&gt;

&lt;pre class=&quot;brush: js&quot;&gt;
 handleKeyChange(event) {
  this.searchKey = event.target.value;
 }
&lt;/pre&gt;

&lt;p&gt;This will cause the findContacts method to fire whenever the value in the input is changed.&lt;/p&gt;

&lt;p&gt;Note that it is the assignment to this.searchKey that causes the event to fire - it looks like the binding from the HTML is 1-way.  I admit that I need to investigate this further.&lt;/p&gt;

&lt;h2&gt;Events do not require configuration to be implemented&lt;/h2&gt;

&lt;p&gt;Events work in a completely different way - but then that’s not a problem - Application and Component events were different enough to cause headaches previously.  The model is actually much simpler.&lt;/p&gt;

&lt;p&gt;The example in the above referenced repository to look at is ‘PubSub’.&lt;/p&gt;

&lt;p&gt;It’s much too involved to into detail here, but the result is that you need to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Implement a Component that acts as the messenger (implementing registerListener, unregisterListener and fireEvent)&lt;/li&gt;
&lt;li&gt;Any component that wants to fire an event, or listen for an event will import that component to do so, firing events or registering listeners.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This would seem to imply that (at least a certain amount of) state within components is shared - looking like those defined with &#39;const&#39;&lt;/p&gt;

&lt;p&gt;Whatever the precise nature of the implementation, a pure Javascript solution is surely one that anyone involved in OO development will welcome.&lt;/p&gt;

&lt;p&gt;I suspect that, in a later release, this will become a standard component.&lt;/p&gt;

&lt;h2&gt;Summary&lt;/h2&gt;

&lt;p&gt;Some people will be thinking &quot;Man, glad I didn’t migrate from Classic / Visualforce to Lightning Experience / Components - maybe I’ll just wait a little longer for it all to settle down”.&lt;/p&gt;

&lt;p&gt;You’re wrong - it won’t settle, it’ll continue to evolve and the technologies will be continually replaced by new ones.  Eventually, the jump from what you have to where you need to get to will be so huge that you’ll find it incredibly hard.  There’s a reason why Salesforce pushes out 3 releases a year, whether you want it or not, these technology jumps are just the same.  The more you put it off, the more painful it’ll be.&lt;/p&gt;

&lt;p&gt;The change from Lightning Components to Lightning Web Components is vast - a lot more than a single 3 letter word would have you suspect.  The only real similarities between the two frameworks that I’ve seen up to now are:&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;Curlies are used to bind things&lt;/li&gt;
&lt;li&gt;The Base Lightning Components are the same&lt;/li&gt;
&lt;li&gt;You need to know Javascript&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Other than that, they’re a world apart.&lt;/p&gt;

&lt;p&gt;Also, I couldn’t find any real documentation - only examples - although those examples are a pretty comprehensive starting point.&lt;/p&gt;

&lt;p&gt;Now, obviously it&#39;s early days - we&#39;re in pre-release right now, but what I&#39;ve seen gives me great hope for the framework, it&#39;s a significant step forward and I can&#39;t wait to see what happens next.  I wonder if a Unit Testing framework might follow (I can but hope)&lt;/p&gt;

&lt;p&gt;You could wait, but hey, really, what are you waiting for? Come on, jump in.  The change is exciting...&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/4460394515160592513/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/10846234/4460394515160592513' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/4460394515160592513'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/4460394515160592513'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2018/12/lightning-web-components-dawn-of.html' title='Lightning Web Components - the dawn of (another) new era'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJKWDfU-frt3gi_ubtgJ8msvcKcp6ChPvxN_uLMCR-FqygXX-IabdD7vmbQ3RHXnwvmQRPaiXacC6CoIfaYcyh0hGdV0PQzbjFaqf_ECJjuD2JzQVdrINY6YBtjgISt_4/s113/Rob+Baillie.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-2960761376031441315</id><published>2018-11-28T19:34:00.001+00:00</published><updated>2018-11-28T19:34:31.311+00:00</updated><title type='text'>LinkedIn, and the GDPR age</title><content type='html'>&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;I should start this post by saying I’m neither a lawyer, nor a GDPR expert. &amp;nbsp;Possibly both of those facts will become massively apparent in the text that follows.&lt;/span&gt;&lt;br /&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;Also, I’m not a LinkedIn Premium user - so it’s possible I’m missing something obvious by not having access to it.&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;But anyway, I’ve been thinking about how LinkedIn fits into a GDPR world, and it doesn’t something doesn’t seem quite right to me at the moment.&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;LinkedIn are in the data business, and they’re very good at protecting that asset. &amp;nbsp;They tend to be (quite rightly) pro-active in stopping people from extracting data from their systems and pushing it into their own systems.&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;As such, businesses (recruiters particularly) are encouraged to contact directly within LinkedIn, and they are offered tools to discover people and commence that communication.&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;Unfortunately, this lack of syncing between LinkedIn and in-house systems can cause a big problem with GDPR.&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;That is:&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;span class=&quot;Apple-tab-span&quot; style=&quot;white-space: pre;&quot;&gt; &lt;/span&gt;What happens if someone says to a recruitment organisation - “Please forget me, and do not contact me again”&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;In this situation, the organisation is obliged to ‘remove&#39; them from their systems.&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;At some point in the future another recruiter from the same organisation then finds the person on LinkedIn, without reference to their own systems and messages them using LinkedIn.&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;What happens next?&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;By the letter of the law, the organisation&amp;nbsp;&lt;i&gt;may&lt;/i&gt;&lt;span class=&quot;Apple-converted-space&quot;&gt;&amp;nbsp;&lt;/span&gt;not have done anything wrong.&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;ul style=&quot;caret-color: rgb(0, 0, 0); text-size-adjust: auto;&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;The person is no longer in the organisation’s system, they were found on LinkedIn.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;The person was not sent an e-mail, or phoned, they were messaged within LinkedIn.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;The person has consented to have their data held by LinkedIn for the expressed purpose of being contacted by potential recruiters via the platform.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div style=&quot;caret-color: rgb(0, 0, 0); text-size-adjust: auto;&quot;&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;caret-color: rgb(0, 0, 0); text-size-adjust: auto;&quot;&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;With all this in mind, it&lt;span class=&quot;Apple-converted-space&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;i&gt;may&lt;/i&gt;&lt;span class=&quot;Apple-converted-space&quot;&gt;&amp;nbsp;&lt;/span&gt;be interpreted that it’s fair game to contact anyone on LinkedIn, regardless of their expressed desire not to be contacted by a particular company.&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;caret-color: rgb(0, 0, 0); text-size-adjust: auto;&quot;&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;caret-color: rgb(0, 0, 0); text-size-adjust: auto;&quot;&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;However, whilst this&lt;span class=&quot;Apple-converted-space&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;i&gt;may&lt;/i&gt;&lt;span class=&quot;Apple-converted-space&quot;&gt;&amp;nbsp;&lt;/span&gt;be within the definition of the law, it’s pretty clear it’s not in the spirit of the law.&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;caret-color: rgb(0, 0, 0); text-size-adjust: auto;&quot;&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;caret-color: rgb(0, 0, 0); text-size-adjust: auto;&quot;&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;Note - Again I’m not a GDPR expert, nor a lawyer, so can&#39;t say for certain that it IS within the definition of the law - nor am I asserting that it is - just that I can imagine that it might&amp;nbsp;be interpreted that way by some people.&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;caret-color: rgb(0, 0, 0); text-size-adjust: auto;&quot;&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;caret-color: rgb(0, 0, 0); text-size-adjust: auto;&quot;&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;And this is where things get complicated for LinkedIn. &amp;nbsp;I can see a few outcomes of this, but two of them could be extremely worrying for the future on LinkedIn.&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;caret-color: rgb(0, 0, 0); text-size-adjust: auto;&quot;&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;h3 style=&quot;caret-color: rgb(0, 0, 0); text-size-adjust: auto;&quot;&gt;
&lt;b&gt;&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;Scenario - LinkedIn Premium is seen as an extension of a subscribing organisation’s IT systems.&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;div style=&quot;caret-color: rgb(0, 0, 0); text-size-adjust: auto;&quot;&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;caret-color: rgb(0, 0, 0); text-size-adjust: auto;&quot;&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;It&lt;span class=&quot;Apple-converted-space&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;i&gt;could&lt;/i&gt;&lt;span class=&quot;Apple-converted-space&quot;&gt;&amp;nbsp;&lt;/span&gt;be argued that, whilst LinkedIn is in independent entity, when they provide services to another organisation, their systems then become part of the remit of that subscribing organisation.&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;caret-color: rgb(0, 0, 0); text-size-adjust: auto;&quot;&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;caret-color: rgb(0, 0, 0); text-size-adjust: auto;&quot;&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;I.E. within LinkedIn, any action by a user and the storage of data of that action falls solely within the responsibility of the employer of the user that performs that action. &amp;nbsp;LinkedIn are not responsible for the use of the data in any way.&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;caret-color: rgb(0, 0, 0); text-size-adjust: auto;&quot;&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;caret-color: rgb(0, 0, 0); text-size-adjust: auto;&quot;&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;On first glance, this looks ideal to LinkedIn - no responsibility!&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;caret-color: rgb(0, 0, 0); text-size-adjust: auto;&quot;&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;caret-color: rgb(0, 0, 0); text-size-adjust: auto;&quot;&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;However, that’s not true - if there’s ever a test case that proves this point, then suddenly LinkedIn becomes a big risk to any organisation that uses it.&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;caret-color: rgb(0, 0, 0); text-size-adjust: auto;&quot;&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;caret-color: rgb(0, 0, 0); text-size-adjust: auto;&quot;&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;Over the course of the last 2 years or so, every data holding organisation in the EU has looked carefully at their data retention and use policies and systems and done what they can to protect themselves - in may cases I’m sure they have changed suppliers and systems since the existing systems have not proven up to scratch in the light of GDPR legislation.&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;caret-color: rgb(0, 0, 0); text-size-adjust: auto;&quot;&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;caret-color: rgb(0, 0, 0); text-size-adjust: auto;&quot;&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;Up to now, I’m not sure that many people have scrutinised LinkedIn in the same way.&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;caret-color: rgb(0, 0, 0); text-size-adjust: auto;&quot;&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;caret-color: rgb(0, 0, 0); text-size-adjust: auto;&quot;&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;At the moment it might be argued that LinkedIn is not supplying the tools to subscribers to allow them to comply with the GDPR legislation. &amp;nbsp;For example, I’m not aware of any functionality that allows an organisation to state &quot;I wish to completely forget this person, and ensure that I cannot connect, view data on or contact them without their expressed consent”. &amp;nbsp;If that’s a minimum requirement of any internal system, why would it&lt;span class=&quot;Apple-converted-space&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;not&lt;/b&gt;&lt;span class=&quot;Apple-converted-space&quot;&gt;&amp;nbsp;&lt;/span&gt;be a minimum requirement for LinkedIn?&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;caret-color: rgb(0, 0, 0); text-size-adjust: auto;&quot;&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;caret-color: rgb(0, 0, 0); text-size-adjust: auto;&quot;&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;It could be that once that test case comes, a lot of organisations will take a look at LinkedIn and decide it doesn’t stand up, and it’s no longer worth the risk.&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;caret-color: rgb(0, 0, 0); text-size-adjust: auto;&quot;&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;h3 style=&quot;caret-color: rgb(0, 0, 0); text-size-adjust: auto;&quot;&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;b&gt;Scenario -&amp;nbsp;&lt;/b&gt;&lt;b&gt;LinkedIn, as the data controller, is responsible for the contact made by any users within the system.&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;div style=&quot;caret-color: rgb(0, 0, 0); text-size-adjust: auto;&quot;&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;caret-color: rgb(0, 0, 0); text-size-adjust: auto;&quot;&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;This is potentially even worse for LinkedIn. &amp;nbsp;Since LinkedIn hold the data about people, provide the tools for discovering those people, provide the tools for contacting people, and for relaying those messages, it&amp;nbsp;&lt;i&gt;may&lt;span class=&quot;Apple-converted-space&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/i&gt;be argued that it is up to LinkedIn to provide the mechanism to allow Users to state that they do not wish to be visible to or contacted by a given organisation.&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;caret-color: rgb(0, 0, 0); text-size-adjust: auto;&quot;&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;caret-color: rgb(0, 0, 0); text-size-adjust: auto;&quot;&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;That is, whilst it is another user who is sending the message, it may be that a future test case could state that LinkedIn are responsible for keeping track of who has ‘forgotten’ who.&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;caret-color: rgb(0, 0, 0); text-size-adjust: auto;&quot;&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;caret-color: rgb(0, 0, 0); text-size-adjust: auto;&quot;&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;By not providing that mechanism, and allowing users on the system to make contact when the contact is not welcome and against the target’s wishes, it’s possible that LinkedIn could be argued as being responsible for the unwelcome contact and therefore misuse of data.&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;caret-color: rgb(0, 0, 0); text-size-adjust: auto;&quot;&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;h3 style=&quot;caret-color: rgb(0, 0, 0); text-size-adjust: auto;&quot;&gt;
&lt;b&gt;&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;Summary&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;div style=&quot;caret-color: rgb(0, 0, 0); text-size-adjust: auto;&quot;&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;caret-color: rgb(0, 0, 0); text-size-adjust: auto;&quot;&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;Today, it seems that LinkedIn is in a bit of limbo.&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;caret-color: rgb(0, 0, 0); text-size-adjust: auto;&quot;&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;caret-color: rgb(0, 0, 0); text-size-adjust: auto;&quot;&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;There may be a recognised way to use LinkedIn in the GDPR era - find someone, check in my system that I’m allowed to contact them, go back to LinkedIn and contact them - but in order for that to work it requires the due diligence of recruiters to ensure that the law isn’t broken.&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;caret-color: rgb(0, 0, 0); text-size-adjust: auto;&quot;&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;caret-color: rgb(0, 0, 0); text-size-adjust: auto;&quot;&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;Realistically, something will have to change, or that test case is coming; at some point, someone&lt;span class=&quot;Apple-converted-space&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;is&lt;/b&gt;&lt;span class=&quot;Apple-converted-space&quot;&gt;&amp;nbsp;&lt;/span&gt;going to get an email that is going to break the limbo.&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;caret-color: rgb(0, 0, 0); text-size-adjust: auto;&quot;&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;caret-color: rgb(0, 0, 0); text-size-adjust: auto;&quot;&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;When that happens, I wonder which way it will go..?&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/2960761376031441315/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/10846234/2960761376031441315' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/2960761376031441315'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/2960761376031441315'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2018/11/linkedin-and-gdpr-age.html' title='LinkedIn, and the GDPR age'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJKWDfU-frt3gi_ubtgJ8msvcKcp6ChPvxN_uLMCR-FqygXX-IabdD7vmbQ3RHXnwvmQRPaiXacC6CoIfaYcyh0hGdV0PQzbjFaqf_ECJjuD2JzQVdrINY6YBtjgISt_4/s113/Rob+Baillie.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-5431440044518739728</id><published>2018-10-19T15:49:00.003+01:00</published><updated>2018-10-19T15:49:41.354+01:00</updated><title type='text'>Things I still believe in</title><content type='html'>&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;Over 10 years ago I wrote a blog post on things that I believe in - as a developer, and when I re-read it recently I was amazed at how little has changed.&lt;/span&gt;&lt;br /&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;I&#39;m not sure if that&#39;s a good thing, or a bad thing - but it&#39;s certainly a thing.&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;Anyway - here&#39;s that list - slightly updated for 2018... it you&#39;ve seen my talk on Unit Testing recently, you might recognise a few entries.&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;(opinions are my own, yada yada yada)&lt;/span&gt;&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;It&#39;s easier to re-build a system from its tests than to re-build the tests from their system.&lt;/span&gt;&lt;/li&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;
&lt;li&gt;&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;You can measure code complexity, adherence to standards and test coverage; you can&#39;t measure quality of design.&lt;/span&gt;&lt;/li&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;
&lt;li&gt;&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;Formal and flexible are not mutually exclusive.&lt;/span&gt;&lt;/li&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;
&lt;li&gt;&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;The tests should pass, first time, every time (unless you&#39;re changing them or the code).&lt;/span&gt;&lt;/li&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;
&lt;li&gt;&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;Test code is production code and it deserves the same level of care.&lt;/span&gt;&lt;/li&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;
&lt;li&gt;&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;Prototypes should always be thrown away.&lt;/span&gt;&lt;/li&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;
&lt;li&gt;&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;Documentation is good, self documenting code is better, code that doesn&#39;t need documentation is best.&lt;/span&gt;&lt;/li&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;
&lt;li&gt;&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;If you&#39;re getting bogged down in the process then the process is wrong.&lt;/span&gt;&lt;/li&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;
&lt;li&gt;&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;Agility without structure is just hacking.&lt;/span&gt;&lt;/li&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;
&lt;li&gt;&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;Pair programming allows good practices to spread.&lt;/span&gt;&lt;/li&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;
&lt;li&gt;&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;Pair programming allows bad practices to spread.&lt;/span&gt;&lt;/li&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;
&lt;li&gt;&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;Team leaders should be inside the team, not outside it.&lt;/span&gt;&lt;/li&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;
&lt;li&gt;&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;Project Managers are there to facilitate the practice of developing software, not to control it.&lt;/span&gt;&lt;/li&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;
&lt;li&gt;&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;Your customers are not idiots; they always know their business far better than you ever will.&lt;/span&gt;&lt;/li&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;
&lt;li&gt;&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;A long list of referrals for a piece of software does not increase the chances of it being right for you, and shouldn&#39;t be considered when evaluating it.&lt;/span&gt;&lt;/li&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;
&lt;li&gt;&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;You can&#39;t solve a problem until you know what the problem is.  You can&#39;t answer a question until the question&#39;s been asked.&lt;/span&gt;&lt;/li&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;
&lt;li&gt;&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;Software development is not complex by accident, it&#39;s complex by essence.&lt;/span&gt;&lt;/li&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;
&lt;li&gt;&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;Always is never right, and never is always wrong.&lt;/span&gt;&lt;/li&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;
&lt;li&gt;&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;Interesting is not the same as useful.&lt;/span&gt;&lt;/li&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;
&lt;li&gt;&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;Clever is not the same as right.&lt;/span&gt;&lt;/li&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;
&lt;li&gt;&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;The simplest thing that will work is not always the same as the easiest thing that will work.&lt;/span&gt;&lt;/li&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;
&lt;li&gt;&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;It&#39;s easier to make readable code correct than it is to make clever code readable.&lt;/span&gt;&lt;/li&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;
&lt;li&gt;&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;If you can&#39;t read your tests, then you can&#39;t read your documentation.&lt;/span&gt;&lt;/li&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;
&lt;li&gt;&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;There&#39;s no better specification document than the customer&#39;s voice.&lt;/span&gt;&lt;/li&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;
&lt;li&gt;&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;You can&#39;t make your brain bigger, so make your code simpler.&lt;/span&gt;&lt;/li&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;
&lt;li&gt;&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;Sometimes multiple exit points are OK.  The same is not true of multiple entry points.&lt;/span&gt;&lt;/li&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;
&lt;li&gt;&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;Collective responsibility means that everyone involved is individually responsible for everything.&lt;/span&gt;&lt;/li&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;
&lt;li&gt;&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;Sometimes it&#39;s complex because it needs to be; but you should never be afraid to double check.&lt;/span&gt;&lt;/li&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;
&lt;li&gt;&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;If every time you step forward you get shot down you&#39;re fighting for the wrong army.&lt;/span&gt;&lt;/li&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;
&lt;li&gt;&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;If you&#39;re always learning you&#39;re never bored.&lt;/span&gt;&lt;/li&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;
&lt;li&gt;&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;There are no such things as &quot;Best Practices&quot;.  Every practice can be improved upon.&lt;/span&gt;&lt;/li&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;
&lt;li&gt;&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;Nothing is exempt from testing.  Not even database upgrades or declarative tools.&lt;/span&gt;&lt;/li&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;
&lt;li&gt;&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;It&#39;s not enough to collect data, you need to analyse, understand and act upon that data once you have it.&lt;/span&gt;&lt;/li&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;
&lt;li&gt;&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;A long code freeze means a broken process.&lt;/span&gt;&lt;/li&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;
&lt;li&gt;&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;A test hasn&#39;t passed until it has failed.&lt;/span&gt;&lt;/li&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;
&lt;li&gt;&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;A test that can&#39;t fail isn&#39;t a test.&lt;/span&gt;&lt;/li&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;
&lt;li&gt;&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;If you give someone a job, you can&#39;t guarantee they&#39;ll do it well; If you give someone two jobs you can guarantee they&#39;ll do both badly&lt;/span&gt;&lt;/li&gt;
&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;
&lt;li&gt;&lt;span style=&quot;font-family: Arial, Helvetica, sans-serif;&quot;&gt;Every meeting should start with a statement on its purpose and context, even if everyone in the meeting already knows.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/5431440044518739728/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/10846234/5431440044518739728' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/5431440044518739728'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/5431440044518739728'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2018/10/things-i-still-believe-in.html' title='Things I still believe in'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJKWDfU-frt3gi_ubtgJ8msvcKcp6ChPvxN_uLMCR-FqygXX-IabdD7vmbQ3RHXnwvmQRPaiXacC6CoIfaYcyh0hGdV0PQzbjFaqf_ECJjuD2JzQVdrINY6YBtjgISt_4/s113/Rob+Baillie.png'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-5811507856396250080</id><published>2018-10-03T13:56:00.002+01:00</published><updated>2018-10-10T14:41:42.657+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="development"/><category scheme="http://www.blogger.com/atom/ns#" term="Javascript"/><category scheme="http://www.blogger.com/atom/ns#" term="promises"/><category scheme="http://www.blogger.com/atom/ns#" term="salesforce"/><title type='text'>Promises and Lightning Components</title><content type='html'>In 2015, the ECMA specification &lt;a href=&quot;https://www.ecma-international.org/ecma-262/6.0/#sec-promise-objects&quot;&gt;included the introduction of Promises&lt;/a&gt;, and finally (pun intended) the Javascript world had a way of escaping from callback hell and moving towards a much richer syntax for asynchronous processes.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;So, what are promises?&lt;/h3&gt;
&lt;br/&gt;
In short, it’s a syntax that allows you to specify callbacks that should execute when a function either ’succeeds’ or ‘fails’ (is resolved, or rejected, in Promise terminology).&lt;br /&gt;
&lt;br /&gt;
For many, they&#39;re a way of implementing callbacks in a way that makes a little more sense syntactically, but for others it&#39;s a new way of looking at how asynchronous code can be structured that reduces the dependancies between them and provides you with some pretty clever mechanisms.&lt;br/&gt;&lt;br/&gt;
However, this article isn’t about &lt;b&gt;what&lt;/b&gt; promises are, but rather:&lt;br /&gt;
&lt;br/&gt;
&lt;h2&gt;How can Promises be used in Lightning Components, and why you would want to?&lt;/h2&gt;
&lt;br /&gt;
&lt;div class=&quot;aside&quot;&gt;
As with any new feature of Javascript, &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise#Browser_compatibility&quot;&gt;make sure you double check the browser compatibility&lt;/a&gt; to make sure it covers your target brower before implementing anything.
&lt;/div&gt;
&lt;br /&gt;
If you want some in depth info on what they are, the best introduction I’ve found is &lt;a href=&quot;https://developers.google.com/web/fundamentals/primers/promises&quot;&gt;this article on developers.google.com&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;
In addition, Salesforce have provided some very limited documentation on how to use them in Lightning, &lt;a href=&quot;https://developer.salesforce.com/docs/atlas.en-us.lightning.meta/lightning/js_promises.htm&quot;&gt;here&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Whilst the documentations&#39;s inclusion can give us hope (Salesforce knows what Promises are and expect them to be used), the documentation itself is pretty slim and doesn’t really go into any depth on when you would use them.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;When to use Promises&lt;/h3&gt;
&lt;br/&gt;
Promises are the prime candidate for use when executing anything that is asynchronous, and there’s an argument to say that any asynchronous Javascript that you write should return a Promise.&lt;br /&gt;
&lt;br /&gt;
For Lightning Components, the most common example is probably when calling Apex.&lt;br/&gt;
&lt;br /&gt;
The standard pattern for Apex would be something along the lines of:&lt;br /&gt;
&lt;br/&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
getData : function( component ) {
    let action = component.get(“c.getData&quot;);
        
    action.setCallback(this, function(response) {
            
        let state = response.getState();

        if (state === &quot;SUCCESS&quot;) {
            let result = response.getReturnValue();
            // do your success thing
        }
        else if (state === &quot;INCOMPLETE&quot;) {
            // do your incomplete thing
        }
        else if (state === &quot;ERROR&quot;) {
            // do your error thing
        }
    });
    $A.enqueueAction(action);
}&lt;/pre&gt;
&lt;br/&gt;
In order to utilise Promises in a such a function you would:
&lt;ol&gt;
  &lt;li&gt;Ensure the function returned a Promise object&lt;/li&gt;
  &lt;li&gt;Call &#39;resolve&#39; or &#39;reject&#39; based on whether the function was successful&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
getData : function( component ) {
    return new Promise( $A.getCallback(
        ( resolve, reject ) =&gt; {

        let action = component.get(“c.getData&quot;);
    
        action.setCallback(this, function(response) {
            
            let state = response.getState();

            if (state === &quot;SUCCESS&quot;) {
                let result = response.getReturnValue();
                // do your success thing
                resolve();
            }
            else if (state === &quot;INCOMPLETE&quot;) {
                // do your incomplete thing
                reject();
            }
            else if (state === &quot;ERROR&quot;) {
                // do your error thing
                reject();
            }
        });
        $A.enqueueAction(action);
    });
}&lt;/pre&gt;
&lt;br/&gt;
You would then call the helper method in the same way as usual&lt;/br&gt;
&lt;br/&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
doInit : function( component, event, helper ) {
    helper.getData();
}
&lt;/pre&gt;
&lt;br/&gt;
So, what are we doing here?&lt;br/&gt;
&lt;/br&gt;
We have updated the helper function so that it now returns a Promise that is constructed with a new function that has two parameters &#39;resolve&#39; and &#39;reject&#39;.  When the function is called, the Promise is returned and the function that we passed in is immediately executed.&lt;br/&gt;&lt;br/&gt;

When our function reaches its notional &#39;success&#39; state (inside the &#39;state == &quot;SUCCESS&quot; section), we call the &#39;resolve&#39; function that is passed in.&lt;br/&gt;&lt;br/&gt;

Similarly, when we get to an error condition, we call &#39;reject&#39;.&lt;br/&gt;&lt;br/&gt;

In this simple case, you&#39;ll find it hard to see where &#39;resolve&#39; and &#39;reject&#39; are defined - because they&#39;re not.  In this case the Promise will create an empty function for you and the Promise will essentially operate as if it wasn&#39;t there at all.  The functionality hasn&#39;t changed.&lt;br/&gt;&lt;br/&gt;

&lt;div class=&quot;aside&quot;&gt;
Aside - if you&#39;re unfamiliar with the &#39;Arrow Function&#39; notation - E.g. () =&gt; { doThing() } - then look &lt;a href=&quot;https://www.sitepoint.com/es6-arrow-functions-new-fat-concise-syntax-javascript/&quot;&gt;here&lt;/a&gt;
 or &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions&quot;&gt;here&lt;/a&gt;.  And don&#39;t forget to &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions#Browser_compatibility&quot;&gt;check the browser compatibility&lt;/a&gt;.
&lt;/div&gt;

&lt;br/&gt;

&lt;h3&gt;So the obvious question is.. Why?&lt;/h3&gt;
&lt;br/&gt;
What does a Promise give you in such a situation?&lt;br/&gt;&lt;br/&gt;

Well, if all you are doing it calling a single function that has no dependant children, then nothing.  But let&#39;s say that you wanted to call &quot;getConfiguration&quot;, which called some Apex, and then *only once that was complete* you called &quot;getData&quot;.&lt;br/&gt;&lt;br/&gt;

Without Promises, you&#39;d have 2 obvious solutions:
&lt;ol&gt;
&lt;li&gt;Call &quot;getData&quot; from the &#39;Success&#39; path of &quot;getConfiguration&quot;.&lt;/li&gt;
&lt;li&gt;Pass &quot;getData&quot; in as a callback on &quot;getConfiguration&quot; and call the callback in the &#39;Success&#39; path of &quot;getConfiguration&quot;&lt;/li&gt;
&lt;/ol&gt;

Neither of these solutions are ideal, though the second is far better than the first.&lt;br/&gt;&lt;br/&gt;

That is - in the first we introduce an explicit dependancy between getConfiguration and getData.  Ideally, this would not be expressed in getConfiguration, but rather in the doInit (or a helper function called by doInit).  It is *that* function which decides that the dependancy is important.&lt;br/&gt;&lt;br/&gt;

The second solution *looks* much better (and is), but it&#39;s still not quite right.  We now have an extra parameter on getConfiguration for the callback.  We *should* also have another callback for the failure path - otherwise we are expressing that only success has a further dependancy, which is a partial leaking of knowledge.&lt;/br&gt;&lt;br/&gt;

&lt;h3&gt;Fulfilling your Promise - resolve and reject&lt;/h3&gt;
&lt;br/&gt;

When we introduce Promises, we introduce the notion of &#39;then&#39;.  That is, when we &#39;call&#39; the Promise, we are able to state that something should happen on &#39;resolve&#39; (success) or &#39;reject&#39; (failure), and we do it from *outside* the called function.&lt;br/&gt;&lt;br/&gt;

Or, to put it another way, &#39;then&#39; allows us to define the functions &#39;resolve&#39; and &#39;reject&#39; that will get passed into our Promise&#39;s function when it is constructed.&lt;br/&gt;&lt;br/&gt;

E.g.&lt;br/&gt;&lt;br/&gt;
We can pass a single function into &#39;then&#39;, and this will be the &#39;resolve&#39; function that gets called on success.&lt;br/&gt;
&lt;br/&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
doInit : function( component, event, helper ) {
    helper.getConfiguration( component )
      .then( () =&gt; { helper.getData( component ) } );
}
&lt;/pre&gt;
&lt;br/&gt;
Or, if we wanted a failure path that resulted in us calling &#39;helper.setError&#39;, we would pass a second function, which will become the &#39;reject&#39; function.&lt;br/&gt;
&lt;br/&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
doInit : function( component, event, helper ) {
    helper.getConfiguration( component )
      .then( () =&gt; { helper.getData( component ) }
           , () =&gt; { helper.setError( component ) } );
}
&lt;/pre&gt;
&lt;br/&gt;
&lt;div class=&quot;aside&quot;&gt;
Aside - It&#39;s possible that the functions should be wrapped in a call to &#39;$A.getCallback&#39;.  You will have seen this in the definition of the Promise above.  This is to ensure that any callback is guaranteed to remain within the context of the Lightning Framework, as defined &lt;a href=&quot;https://developer.salesforce.com/docs/atlas.en-us.lightning.meta/lightning/js_cb_mod_ext_js.htm&quot;&gt;here&lt;/a&gt;.  I&#39;ve not witnessed any problem with not including it, although it&#39;s worth bearing in mind if you start to get issues on long running operations.
&lt;/div&gt;
&lt;br/&gt;
Now, this solution isn&#39;t vastly different to passing the two functions directly into the helper function.  E.g. like this:&lt;br/&gt;
&lt;br/&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
doInit : function( component, event, helper ) {
    helper.getConfiguration( component
                           , () =&gt; { helper.getData( component ) }
                           , () =&gt; { helper.setError( component ) } );
}
&lt;/pre&gt;
&lt;br/&gt;
And whilst I might say that I personally don&#39;t like the act of passing in the two callbacks directly into the function, personal dislike is probably not a good enough reason to use a new language feature in a business critical system.&lt;br/&gt;&lt;br/&gt;

So is there a better reason for doing it?&lt;br/&gt;&lt;br/&gt;

&lt;h3&gt;Promising everything, or just something&lt;/h3&gt;
&lt;br/&gt;

Thankfully, Promises are more than just a mechanism for callbacks, they are a generic mechanism for *guaranteeing* that &#39;settled&#39; (fulfilled or rejected) Promises result in a specified behaviour occurring once certain states occur.&lt;br/&gt;&lt;br/&gt;

When using a simple Promise, we are simply saying that the behaviour should be that the &#39;resolve&#39; or &#39;reject&#39; functions get called.  But that&#39;s not the only option&lt;br/&gt;&lt;br/&gt;.

For example, we also have:
&lt;table&gt;
&lt;tr&gt;&lt;td&gt;Promise.all&lt;/td&gt;&lt;td&gt;Will &#39;resolve&#39; only when *all* the passed in Promises resolve, and will &#39;reject&#39; if and when *any* of the Promises reject.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Promise.race&lt;/td&gt;&lt;td&gt;Will &#39;resolve&#39; or &#39;reject&#39; when the first Promise to respond comes back with a &#39;resolve&#39; or &#39;reject&#39;.&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;

Once we add that to the mix, we can do something a little clever...&lt;br/&gt;&lt;br/&gt;

How about having the component load with a &#39;loading spinner&#39; that is only switched off when all three calls to Apex respond with success:&lt;br/&gt;
&lt;br/&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
doInit : function( component, event, helper ) {
    Promise.all( [ helper.getDataOne( component )
                 , helper.getDataTwo( component )
                 , helper.getDataThree( component ) ] )
           .then( () =&gt; { helper.setIsLoaded( component ) } );
}
&lt;/pre&gt;
&lt;br/&gt;
Or even better - how about we call getConfiguration, then once that’s done we call each of the getData functions, and only when all three of those are finished do we set the flag:&lt;br/&gt;
&lt;br/&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
doInit : function( component, event, helper ) {
    helper.getConfiguration( component )
        .then( Promise.all( [ helper.getDataOne( component )
                            , helper.getDataTwo( component )
                            , helper.getDataThree( component ) ] )
                      .then( () =&gt; { helper.setIsLoaded( component ) } )
             );
}
&lt;/pre&gt;
&lt;br/&gt;
Now, just for a second, think about how you would do that without Promises...&lt;br /&gt;
</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/5811507856396250080/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/10846234/5811507856396250080' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/5811507856396250080'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/5811507856396250080'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2018/10/promises-and-lightning-components.html' title='Promises and Lightning Components'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJKWDfU-frt3gi_ubtgJ8msvcKcp6ChPvxN_uLMCR-FqygXX-IabdD7vmbQ3RHXnwvmQRPaiXacC6CoIfaYcyh0hGdV0PQzbjFaqf_ECJjuD2JzQVdrINY6YBtjgISt_4/s113/Rob+Baillie.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-875320101438317915</id><published>2014-12-13T10:26:00.001+00:00</published><updated>2014-12-13T10:32:11.102+00:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="design"/><category scheme="http://www.blogger.com/atom/ns#" term="development"/><category scheme="http://www.blogger.com/atom/ns#" term="Principles"/><category scheme="http://www.blogger.com/atom/ns#" term="processes"/><title type='text'>Throw it away - Why you shouldn&amp;#39;t keep your POC</title><content type='html'>&lt;p dir=&quot;ltr&quot;&gt;&quot;Proof of Concepts&quot; are a vital part of many projects, particularly towards the beginning of the project lifecycle, or even in the pre-business case stages.&lt;/p&gt;
&lt;p dir=&quot;ltr&quot;&gt;They are crucial for ensuring that facts are gathered before some particularly risk decisions are made.&amp;#160; Technical or functional, they can address many different concerns and each one can be different, but they all have one thing in common.&amp;#160; They serve to answer questions.&lt;/p&gt;
&lt;p dir=&quot;ltr&quot;&gt;It can be tempting, whilst answering these questions to become attached to the code that you generate.&lt;/p&gt;
&lt;p dir=&quot;ltr&quot;&gt;I would strongly argue that you should almost &lt;b&gt;never&lt;/b&gt; keep the code that you build during a POC.&amp;#160; Certainly not to put into a production system.&lt;/p&gt;
&lt;p dir=&quot;ltr&quot;&gt;I&#39;d go so far as to say that planning to keep the code it is often damaging to the proof of concept; planning to throw the code away is liberating, more efficient and makes proof of concepts more effective by focussing the minds on the questions that require answers..&lt;/p&gt;
&lt;p dir=&quot;ltr&quot;&gt;&lt;b&gt;Why do we set out on a proof of concept?&lt;/b&gt;&lt;/p&gt;
&lt;p dir=&quot;ltr&quot;&gt;The purpose of a proof of concept is to (by definition):&lt;/p&gt;
&lt;p dir=&quot;ltr&quot;&gt;&amp;#160; * Prove:&amp;#160; Demonstrate the truth or existence of &lt;i&gt;something&lt;/i&gt; by evidence or argument.&lt;br&gt;
&amp;#160; * Concept: An idea, a plan or intention.&lt;/p&gt;
&lt;p dir=&quot;ltr&quot;&gt;In most cases, the concept being proven is a technical one.&amp;#160; For example:&lt;br&gt;
&amp;#160; * Will this language be suitable for building x?&lt;br&gt;
&amp;#160; * Can I embed x inside y and get them talking to each other?&lt;br&gt;
&amp;#160; * If I put product x on infrastructure y will it basically stand up?&lt;/p&gt;
&lt;p dir=&quot;ltr&quot;&gt;They can also be functional, but the principles remain the same for both.&lt;/p&gt;
&lt;p dir=&quot;ltr&quot;&gt;It&#39;s hard to imagine a proof of concept that cannot be phrased as one or more questions.&amp;#160; In a lot of cases I&#39;d suggest that there&#39;s only really one important question with a number of ancillary questions that are used to build a body of evidence.&lt;/p&gt;
&lt;p dir=&quot;ltr&quot;&gt;The implication of embarking on a proof of concept is that when you start you don&#39;t know the answer to the questions you&#39;re asking.&amp;#160; If you *do* already know the answers, then the POC is of no value to you.&lt;/p&gt;
&lt;p dir=&quot;ltr&quot;&gt;By extension, there is the implication that the questions posed require to be answered as soon as possible in order to support a decision.&amp;#160; If that&#39;s not the case then, again, the POC is probably not of value to you.&lt;/p&gt;
&lt;p dir=&quot;ltr&quot;&gt;As such, &lt;b&gt;the only thing that the POC should aim to achieve is to answer the question posed and to do so as quickly as possible.&lt;/b&gt;&lt;/p&gt;
&lt;p dir=&quot;ltr&quot;&gt;This is quite different to what we set out to do in our normal software development process.&amp;#160; &lt;/p&gt;
&lt;p dir=&quot;ltr&quot;&gt;We normally know the answer to the main question we&#39;re asking (How do we functionally provide a solution to this problem / take advantage of this opportunity), and most of the time is spent focussed on building something that is solid, performs well and generally good enough to live in a production environment - in essence, not answering the question, but producing software.&lt;/p&gt;
&lt;p dir=&quot;ltr&quot;&gt;&lt;b&gt;What process do we follow when embarking on a proof of concept?&lt;/b&gt;&lt;/p&gt;
&lt;p dir=&quot;ltr&quot;&gt;Since the aim of a POC is distinct from what we normally set out to achieve, the process for a POC is intrinsically different to that for the development of a production system.&lt;/p&gt;
&lt;p dir=&quot;ltr&quot;&gt;With the main question in mind, you often follow an almost scientific process.&amp;#160; You put forward a hypothesis, you set yourself tasks that are aimed at collecting evidence that will support or deny that hypothesis, you analyse the data, put forward a revised hypothesis and you start again.&lt;/p&gt;
&lt;p dir=&quot;ltr&quot;&gt;You keep going round in this routine until you feel you have an answer to the question and enough evidence to back that answer up.&amp;#160; It is an entirely exploratory process.&lt;/p&gt;
&lt;p dir=&quot;ltr&quot;&gt;Often, you will find that you spend days following avenues that don&#39;t lead anywhere, backtrack and reassess, following a zig-zag path through a minefield of wrong answers until you reach the end point.&amp;#160; In this kind of situation, the code you have produced is probably one of the most barnacle riddled messes you have every produced.&lt;/p&gt;
&lt;p dir=&quot;ltr&quot;&gt;But that&#39;s OK.&amp;#160; The reason for the POC wasn&#39;t to build a codebase, it was to provide an answer to a question and a body of evidence that supports that answer.&lt;/p&gt;
&lt;p dir=&quot;ltr&quot;&gt;To illustrate:&lt;/p&gt;
&lt;p dir=&quot;ltr&quot;&gt;&lt;b&gt;Will this language be suitable for building x?&lt;/b&gt;&lt;/p&gt;
&lt;p dir=&quot;ltr&quot;&gt;You may need to check things like that you can build the right type of user interfaces, that APIs can be created, that there are ways of organising code that makes sense for the long term maintenance for the system.&lt;/p&gt;
&lt;p dir=&quot;ltr&quot;&gt;You probably don&#39;t need to build a completely functional UI, create a fully functioning API with solid error handling or define the full set of standards for implementing a production quality system in the given language.&lt;/p&gt;
&lt;p dir=&quot;ltr&quot;&gt;That said, if you were building a production system in the language you wouldn&#39;t dream of having in incomplete UI, or an API that doesn&#39;t handle errors completely or just knocking stuff together in an ad-hoc manner.&lt;/p&gt;
&lt;p dir=&quot;ltr&quot;&gt;&lt;b&gt;Can I embed x inside y and get them talking to each other&lt;/b&gt;&lt;/p&gt;
&lt;p dir=&quot;ltr&quot;&gt;You will probably need to define a communication method and prove that it basically works.&amp;#160; Get something up and running that is at least reasonably functional in the &quot;through the middle&quot; test case.&lt;/p&gt;
&lt;p dir=&quot;ltr&quot;&gt;You probably don&#39;t need to develop an architecture that is clean with separation of concerns that means the systems are properly independant and backwards compatible with existing integrations. Or that all interactions are properly captured and that exceptional circumstances are dealt with correctly.&lt;/p&gt;
&lt;p dir=&quot;ltr&quot;&gt;That said, if you were building a production system, you&#39;d need to ensure that you define the full layered architecture, understand the implications of lost messages, prove the level of chat that will occur between the systems.&amp;#160; On top of that you need to know that you don&#39;t impact pre-existing behaviour or APIs.&lt;/p&gt;
&lt;p dir=&quot;ltr&quot;&gt;&lt;b&gt;If I put product x on infrastructure y will it basically stand up?&lt;/b&gt;&lt;/p&gt;
&lt;p dir=&quot;ltr&quot;&gt;You probably need to just get the software on there and run your automated tests.&amp;#160; Maybe you need to prove the performance and so you&#39;ll put together some ad-hoc performance scripts.&lt;/p&gt;
&lt;p dir=&quot;ltr&quot;&gt;You probably don&#39;t need to prove that your release mechanism is solid and repeatable, or ensure that your automated tests cover some of the peculiarities of the new infrastructure, or that you have a good set of long term performance test scripts that drop into your standard development and deployment process.&lt;/p&gt;
&lt;p dir=&quot;ltr&quot;&gt;That said, if you were building a production system, you&#39;d need to know exactly how the deployments worked, fit it into your existing continuous delivery suite, performance test and analyse on an automated schedule.&lt;/p&gt;
&lt;p dir=&quot;ltr&quot;&gt;&lt;b&gt;Production development and Proof of Concept development is not the same&lt;/b&gt;&lt;/p&gt;
&lt;p dir=&quot;ltr&quot;&gt;The point is, when you are building a production system you have to do a lot of leg-work; you know you can validate all the input being submitted in a form, or coming through an API - you just have to do it.&lt;/p&gt;
&lt;p dir=&quot;ltr&quot;&gt;You need to ensure that the functionality you&#39;re providing works in the majority of use-cases, and if you&#39;re working in a TDD environment then you will prove that by writing automated tests before you&#39;ve even started creating that functionality.&lt;/p&gt;
&lt;p dir=&quot;ltr&quot;&gt;When you&#39;re building a proof of concept, not only should these tests be a lower priority, I would argue that they should be *no priority whatsoever*, unless they serve to test the concept that you&#39;re trying to prove.&lt;/p&gt;
&lt;p dir=&quot;ltr&quot;&gt;That is,&amp;#160; you&#39;re not usually trying to ensure that this piece of code works in all use-cases, but rather that this concept works in the general case with a degree of certainty that you can *extend* it to all cases.&lt;/p&gt;
&lt;p dir=&quot;ltr&quot;&gt;Ultimately, the important deliverable of a POC is proof that the concept works, or doesn&#39;t work; the exploration of ideas and the conclusion you come to; the journey of discovery and the destination of the answer to the question originally posed.&lt;/p&gt;
&lt;p dir=&quot;ltr&quot;&gt;That is &lt;b&gt;intellectual currency&lt;/b&gt;, not software.&amp;#160; The important delivery of a production build is the software that is built.&lt;/p&gt;
&lt;p dir=&quot;ltr&quot;&gt;That is the fundamental difference, and why you should throw your code away.&lt;/p&gt;
</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/875320101438317915/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/10846234/875320101438317915' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/875320101438317915'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/875320101438317915'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2014/12/throw-it-away-why-you-shouldn-keep-your.html' title='Throw it away - Why you shouldn&amp;#39;t keep your POC'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJKWDfU-frt3gi_ubtgJ8msvcKcp6ChPvxN_uLMCR-FqygXX-IabdD7vmbQ3RHXnwvmQRPaiXacC6CoIfaYcyh0hGdV0PQzbjFaqf_ECJjuD2JzQVdrINY6YBtjgISt_4/s113/Rob+Baillie.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-638950848296882161</id><published>2014-10-09T11:56:00.000+01:00</published><updated>2014-10-09T12:02:31.003+01:00</updated><title type='text'>The opportunity cost of delaying software releases</title><content type='html'>Let me paint a simple picture (but with lots of numbers).&lt;br /&gt;
&lt;br /&gt;
Some software has been built. &amp;nbsp;It generates revenue (or reduces cost) associated with sales, but the effect is not immediate. &amp;nbsp;It could be the implementation of a process change that takes a little time to bed in, or the release of a new optional extra that not everyone will want immediately.&lt;br /&gt;
&lt;br /&gt;
It is expected that when it is initially released there’ll be a small effect. &amp;nbsp;Over the next 6 months there will be an accelerating uptake until it reaches saturation point and levels off.&lt;br /&gt;
&lt;br /&gt;
Nothing particularly unusual about that plan. &amp;nbsp;It probably describes a lot of small scale software projects.&lt;br /&gt;
Now let’s put some numbers against that.&lt;br /&gt;
&lt;br /&gt;
At saturation point it’s expected to generate / save an amount equal to 2% of the total revenue of the business. &amp;nbsp;It might be an ambitious number, but it’s not unrealistic.&lt;br /&gt;
&lt;br /&gt;
The business initially generates £250k a month, and experiences steady growth of around 10% a year.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;
What does the revenue generation of that software look like over the first 12 months?&lt;/h3&gt;
&lt;br /&gt;
It’s pretty easy to calculate, plugging in some percentages that reflect the uptake curve:&lt;br /&gt;
&lt;br /&gt;
&lt;style&gt;
&lt;/style&gt;
&lt;table class=&quot;RealTable&quot; style=&quot;margin-top: 0px;&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: right;&quot;&gt;Period&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;Original Business Revenue&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;Software Revenue Generation&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;Additional Revenue&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: right;&quot;&gt;1&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;£250,000.00&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;0.2%&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;£500.00&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: right;&quot;&gt;2&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;£252,500.00&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;0.5%&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;£1,262.50&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: right;&quot;&gt;3&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;£255,025.00&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;1.1%&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;£2,805.28&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: right;&quot;&gt;4&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;£257,575.25&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;1.6%&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;£4,121.20 &lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: right;&quot;&gt;5&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;£260,151.00&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;1.9%&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;£4,942.87&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: right;&quot;&gt;6&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;£262,752.51&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;2.0%&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;£5,255.05&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: right;&quot;&gt;7&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;£265,380.04&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;2.0%&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;£5,307.60&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: right;&quot;&gt;8&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;£268,033.84&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;2.0%&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;£5,360.68&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: right;&quot;&gt;9&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;£270,714.18&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;2.0%&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;£5,414.28 &lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: right;&quot;&gt;10&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;£273,421.32&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;2.0%&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;£5,468.43&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: right;&quot;&gt;11&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;£276,155.53&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;2.0%&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;£5,523.11&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: right;&quot;&gt;12&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;£278,917.09&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;2.0%&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;£5,578.34&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;Total:&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;£51,539.34&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
Or, shown on a graph:&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjGgu7SYJY22S_YRBKhfz1k2HpbQTXLuQHICLv9qwS02unvS4msx0ImJ6FQngcTwT79CiFogqSoUK84IvnuFRgdzRkHsBMRqXF7e40XE4dGCfqNJhMGh9kTfrRh_w1mUpc2XGYBdQ/s1600/graph1.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjGgu7SYJY22S_YRBKhfz1k2HpbQTXLuQHICLv9qwS02unvS4msx0ImJ6FQngcTwT79CiFogqSoUK84IvnuFRgdzRkHsBMRqXF7e40XE4dGCfqNJhMGh9kTfrRh_w1mUpc2XGYBdQ/s1600/graph1.png&quot; height=&quot;379&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
So, here’s a question:&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;
What is the opportunity cost of delaying the release by 2 months?&lt;/h3&gt;
&lt;br /&gt;
The initial thought might be that the effect isn’t that significant, as the software doesn’t generate a huge amount of cash in the first couple of months.&lt;br /&gt;
&lt;br /&gt;
Modelling it, we end up with this:&lt;br /&gt;
&lt;br /&gt;
&lt;table class=&quot;RealTable&quot; style=&quot;margin-top: 0px;&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;Period&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;Original Business Revenue&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;Software Revenue Generation&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;Additional Revenue&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;1&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;£250,000.00&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;£-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;2&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;£252,500.00&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;£-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;3&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;£255,025.00&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;0.2%&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;£510.05&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;4&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;£257,575.25&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;0.5%&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;£1,287.88&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;5&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;£260,151.00&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;1.1%&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;£2,861.66&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;6&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;£262,752.51&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;1.6%&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;£4,204.04&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;7&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;£265,380.04&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;1.9%&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;£5,042.22&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;8&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;£268,033.84&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;2.0%&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;£5,360.68&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;9&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;£270,714.18&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;2.0%&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;£5,414.28&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;10&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;£273,421.32&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;2.0%&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;£5,468.43&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;11&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;£276,155.53&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;2.0%&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;£5,523.11&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;12&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;£278,917.09&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;2.0%&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;£5,578.34&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;Total:&lt;/td&gt;
    &lt;td style=&quot;text-align: right;&quot;&gt;£41,250.69&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
Let’s show that on a comparative graph, showing monthly generated revenue:&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi4RZqEL0archgNVSXn_0gvSeSJZYnhztdiO04RWDACtG0OqxadhCSKPye6ltN_1uF2RCWd7ZrMX27ehHkeoZGR3fFkAVDQtZZl-eUH5NNOI9tYP6gbNZoOnrNRUA0OqkWuokLMUQ/s1600/graph2.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi4RZqEL0archgNVSXn_0gvSeSJZYnhztdiO04RWDACtG0OqxadhCSKPye6ltN_1uF2RCWd7ZrMX27ehHkeoZGR3fFkAVDQtZZl-eUH5NNOI9tYP6gbNZoOnrNRUA0OqkWuokLMUQ/s1600/graph2.png&quot; height=&quot;384&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Or, even more illustrative, the total generated revenue:&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhf7M5ES6Otkgc6QAB85Szl_s0aiuAoTAherhqyKJ7GCUawk1Xa42MBs0yAIVUPwo52Vyj1o865zdOBooI1SPH7vc4SyHJlTV7cAvwrQfO9yIqVH7vwon1GBHBqvhE_36gETyV-Hw/s1600/graph3.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhf7M5ES6Otkgc6QAB85Szl_s0aiuAoTAherhqyKJ7GCUawk1Xa42MBs0yAIVUPwo52Vyj1o865zdOBooI1SPH7vc4SyHJlTV7cAvwrQfO9yIqVH7vwon1GBHBqvhE_36gETyV-Hw/s1600/graph3.png&quot; height=&quot;364&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;
By releasing 2 months later, we do not lose the first 2 months revenue – we lose the revenue roughly equivalent to P5 and P6.&lt;/h3&gt;
&lt;br /&gt;
Why?&lt;br /&gt;
&lt;br /&gt;
When we release in P3, we don’t immediately get the P3 revenue we would have got. &amp;nbsp;Instead we get something roughly equivalent to P1 (it’s slightly higher because the business generates a little more revenue overall in P3 than it did in P1).&lt;br /&gt;
&lt;br /&gt;
This trend continues in P3 through to P8, where the late release finally reaches saturation point (2 periods later than the early release – of course).&lt;br /&gt;
&lt;br /&gt;
Throughout the whole of P1 to P7 the late release has an opportunity cost associated. &amp;nbsp;That opportunity cost is never recovered later in the software’s lifespan as the revenue / cost we could have generated the effect from is gone.&lt;br /&gt;
&lt;br /&gt;
If the business was not growing, this would amount to a total equal to the last 2 periods of the year.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;
In our specific example, the total cost of delaying the release for 2 months amounts to 20% of the original expected revenue generation for the software project in the first year.&lt;/h3&gt;
&lt;br /&gt;
And this opportunity cost is solely related to the way in which the revenue will be generated; the rate at which the uptake comes in over the first 6 months.&lt;br /&gt;
&lt;br /&gt;
Or to put it another way – in this example, if you were to increase or decrease the revenue of the business or the percentage generation at which you reach saturation point the cost will always be 20%.&lt;br /&gt;
&lt;br /&gt;
So, when you’re thinking of delaying the release of software it’s probably worth taking a look, modelling your expected uptake and revenue generation to calculate just how much that will cost you…&lt;br /&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/638950848296882161/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/10846234/638950848296882161' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/638950848296882161'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/638950848296882161'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2014/10/the-opportunity-cost-of-delaying.html' title='The opportunity cost of delaying software releases'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJKWDfU-frt3gi_ubtgJ8msvcKcp6ChPvxN_uLMCR-FqygXX-IabdD7vmbQ3RHXnwvmQRPaiXacC6CoIfaYcyh0hGdV0PQzbjFaqf_ECJjuD2JzQVdrINY6YBtjgISt_4/s113/Rob+Baillie.png'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjGgu7SYJY22S_YRBKhfz1k2HpbQTXLuQHICLv9qwS02unvS4msx0ImJ6FQngcTwT79CiFogqSoUK84IvnuFRgdzRkHsBMRqXF7e40XE4dGCfqNJhMGh9kTfrRh_w1mUpc2XGYBdQ/s72-c/graph1.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-2184060368678713359</id><published>2013-08-31T16:46:00.001+01:00</published><updated>2013-09-01T08:21:52.983+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="management"/><title type='text'>Redundancies should come with a pay rise</title><content type='html'>&lt;p dir=&quot;ltr&quot;&gt;As far as I can see, there is only one reason why a company should ever make redundancies.&lt;/p&gt;
&lt;p dir=&quot;ltr&quot;&gt;Due to some unforseen circumstances the business has become larger than the market conditions can support and it needs to shrink in order to bring it back in line.&lt;/p&gt;
&lt;p dir=&quot;ltr&quot;&gt;Every other reason is simply a minor variation or a consequence of that underlying reason.&lt;/p&gt;
&lt;p dir=&quot;ltr&quot;&gt;Therefore, if the motivation is clear, and the matter dealt with successfully, then once the redundancies are over the business should be &quot;right sized&quot; (we&#39;ve all heard that term before), and it should be able to carry on operating with the same values, practices and approach that it did prior to the redundancies.&lt;/p&gt;
&lt;p dir=&quot;ltr&quot;&gt;If the business can&#39;t, then I would suggest is that it is not the right size for the market conditions and therefore the job isn&#39;t complete.&lt;/p&gt;
&lt;p dir=&quot;ltr&quot;&gt;OK, there may be some caveats to that, but to my mind this reasoning is sound.&lt;/p&gt;
&lt;p dir=&quot;ltr&quot;&gt;In detail:&lt;/p&gt;
&lt;p dir=&quot;ltr&quot;&gt;When you reduce the headcount of the business you look for the essential positions in the company, keep those, and get rid of the rest.&lt;/p&gt;
&lt;p dir=&quot;ltr&quot;&gt;Once the redundancies are finished you should be left with only the positions you need to keep in order to operate successfully.&lt;/p&gt;
&lt;p dir=&quot;ltr&quot;&gt;It&#39;s tempting to think that you should have a recruitment freeze and not back-fill positions when people leave, but if someone leaves and you don&#39;t need to replace them, then that means you didn&#39;t need that position, in which case you should have made it redundant.&lt;/p&gt;
&lt;p dir=&quot;ltr&quot;&gt;Not back-filling positions is effectively the same as allowing your employees to choose who goes based on their personal motives rather than force the business heads to choose based on the business motives.&amp;#160; This doesn&#39;t make business sense.&lt;/p&gt;
&lt;p dir=&quot;ltr&quot;&gt;So, you need to be decisive and cut as far as you can go without limiting your ability to operate within the current market conditions.&lt;/p&gt;
&lt;p dir=&quot;ltr&quot;&gt;To add to that, recruitment is expensive.&amp;#160; If you&#39;re in a highly skilled market then you&#39;ll likely use an agency. They can easily charge 20% of a salary for a perm head.&amp;#160; On top of that you have the cost of bringing someone up to speed, at a time when you&#39;re running at the minimum size your market will allow.&amp;#160; Plus there&#39;s the cost of inefficiency during the onboarding period as well as the increased chance of the remaining overstretched employees leaving as well.&lt;/p&gt;
&lt;p dir=&quot;ltr&quot;&gt;The upshot is that you really can&#39;t afford to have people leave, it&#39;s so expensive that it jeopardises the extremely hard work you did when you made the redundancies.&lt;/p&gt;
&lt;p dir=&quot;ltr&quot;&gt;There&#39;s a theory I often hear that you can&#39;t have contractors working when the perm heads are being marched out.&amp;#160; That&#39;s a perfectly valid argument if the perm head would be of long term value to you and can do the job that the contract head can do.&amp;#160; But if you need the contractor to do a job that only lasts another 3 months and that person is by far the best or only person you have for the job, then the argument just doesn&#39;t stand up.&amp;#160; Get rid of the perm position now and use the contractor, it&#39;ll be cheaper and more beneficial to the business in the long run.&lt;/p&gt;
&lt;p dir=&quot;ltr&quot;&gt;OK, that&#39;s maybe not the most sentimental of arguments, but why would you worry about hurting the feelings of people who no longer work for you, at the expense of those that still do?&lt;/p&gt;
&lt;p dir=&quot;ltr&quot;&gt;It may even be worse than that - you could be jeopardising the jobs of others that remain by not operating in the most efficient and effective way possible.&lt;/p&gt;
&lt;p dir=&quot;ltr&quot;&gt;Another prime example is maternity cover.&amp;#160; If you need the person on maternity to come back to work then you almost certainly need the person covering them. If it&#39;s early in the maternity leave then you&#39;ll have a long period with limited staff, if it&#39;s late in the leave then you only need the temporary cover for a short period more. Either way you&#39;re overstretching the perm staff left to cover them and risking having them leave.&lt;/p&gt;
&lt;p dir=&quot;ltr&quot;&gt;Finally, there&#39;s the motivation to ensure that the business that remains is running as lean as possible. That costs are as low as they could be. The temptation is to cut the training and entertainments budget to minimum and pull back on the benefits package.&lt;br&gt;
As soon as you do this you fundamentally change the character of the business.&amp;#160; If you always prided yourself on being at the forefront of training then you attracted and kept staff who valued that. If you always had an open tab on a Friday night at the local bar, then you attracted people who valued that.&amp;#160; Whatever it is that you are cutting back on, you are saying to people who valued it that &quot;we no longer want to be as attractive to you as we once were; we do not value you quite as much as we did&quot;. This might not be your intention, but it is the message your staff will hear.&lt;/p&gt;
&lt;p dir=&quot;ltr&quot;&gt;I put it to you that the cheapest way to reduce costs after redundancies is to be completely honest to the staff you keep. Say it was difficult, say that you&#39;re running at minimum and that a lot will be expected of whoever&#39;s left. But tell them that they&#39;re still here because they&#39;re the best of the company and they are vital to the company&#39;s success.&amp;#160; Let them know that the contractors you&#39;ve kept are there because they&#39;re the best people for those positions to ensure that the company succeeds.&amp;#160; Tell them that the contractors will be gone the moment they&#39;re not generating value or when a perm head would be more appropriate.&amp;#160; Make it clear that the company is now at the right size and the last thing you want is for people to leave, because you value them and that if they left it would damage your ability to do business.&lt;/p&gt;
&lt;p dir=&quot;ltr&quot;&gt;Then give them a pay rise and a party to prove it.&lt;/p&gt;
</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/2184060368678713359/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/10846234/2184060368678713359' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/2184060368678713359'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/2184060368678713359'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2013/08/redundancies-should-come-with-pay-rise.html' title='Redundancies should come with a pay rise'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJKWDfU-frt3gi_ubtgJ8msvcKcp6ChPvxN_uLMCR-FqygXX-IabdD7vmbQ3RHXnwvmQRPaiXacC6CoIfaYcyh0hGdV0PQzbjFaqf_ECJjuD2JzQVdrINY6YBtjgISt_4/s113/Rob+Baillie.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-2476745191778814466</id><published>2013-08-29T11:19:00.000+01:00</published><updated>2013-08-29T11:19:14.837+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="design"/><category scheme="http://www.blogger.com/atom/ns#" term="processes"/><title type='text'>Agile and UX can mix</title><content type='html'>&lt;div dir=&quot;ltr&quot;&gt;
User experience design is an agile developer&#39;s worst nightmare. You want to make a change to a system, so you research. You collect usage stats, you analyse hotspots, you review, you examine user journeys, you review, you look at drop off rates, you review. Once you&#39;ve got enough data you start to design. You paper prototype, run through with users, create wireframes, run through with users, build prototypes, run through with users, do spoken journey and video analysis, iterate, iterate, iterate, until finally you have a design.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
Then you get the developers to build it, exactly as you designed it.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
Agile development, on the other hand, is a user experience expert&#39;s worst nightmare. You want to make a change to a system, so you decide what&#39;s the most important bit, and you design and build that - don&#39;t worry how it fits into the bigger picture, show it to the users, move on to the next bit, iterate, iterate, iterate, until finally you have a system.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
Then you get the user experience expert to fix all the clumsy workflows.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
The two approaches are fundamentally opposed.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
Aren&#39;t they?&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
Well, of course, I&#39;m exaggerating for comic effect, but these impressions are only exaggerations - they&#39;re not complete fabrications.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
If you look at what&#39;s going on, both approaches have the same underlying principle - your users don&#39;t know what they want until they see something. Only then do they have something to test their ideas against.&amp;nbsp; Both sides agree, the earlier you get something tangible in front of users and the more appropriate and successful the solution will be.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
The only real difference in the two approaches as described is the balance between scope of design and fullness of implementation. On the UX side the favour is for maximum scope of design and minimal implementation; the agile side favours minimal scope of design and maximum implementation.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
The trick is to acknowledge this difference and bring them closer together, or mitigate against the risks those differences bring.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
Or, the put it another way, the main problem you have with combining these two approaches is the lead up time before development starts.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
In the agile world some people would like to think that developing based on a whim is a great way to work, but the reality is different. Every story that is developed will have gone through some phase of analysis even in the lightest of light touch processes. Not least someone has decided that a problem needs fixing.&amp;nbsp; Even in the most agile of teams there needs to be some due diligence and prioritisation.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
This happens not just at the small scale, but also when deciding which overarching areas of functionality to change. In some organisations there will be a project (not a dirty word), in some a phase, in others a sprint. Whatever its called it&#39;ll be a consistent set of stories that build up to be a fairly large scale change in the system. This will have gone through some kind of appraisal process, and rightly so.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
Whilst I don&#39;t particularly believe in business cases, I do believe in due diligence.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
It is in this phase, the research, appraisal and problem definition stage, that UX&lt;i&gt; &lt;/i&gt;research can start without having a significant impact on the start-up time. Statistics can be gathered and evidence amassed to describe the problem that needs to be addressed. This can form a critical part of the argument to start work.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
In fact, this research can become part the business-as-usual activities of the team and can be used to discover issues that need to be addressed. This &lt;i&gt;can&lt;/i&gt; be as &quot;big process&quot; as you want it to be, just as long as you are willing, and have the resources to pick up the problems that you find, and that you have the agility to react to clear findings as quickly as possible. Basically, you need to avoid being in the situation where you know there&#39;s a problem but you can&#39;t start to fix it because your process states you need to finish your 2 month research phase.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
When you are in this discovery phase there&#39;s nothing wrong with starting to feel out some possible solutions. Ideas that can be used to illustrate the problem and the potential benefits of addressing it. Just as long as the techniques you use do not result in high cost and (to reiterate) a lack of ability to react quickly.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
Whilst I think its OK to use whatever techniques work for you, for me the key to keeping the reaction time down is to keep it lightweight.&amp;nbsp; That is, make sure you&#39;re always doing enough to find out what you need to know, but not so much that it takes you a long time to reach conclusions and start to address them. User surveys, spoken narrative and video recordings, all of which can be done remotely, can be done at any time, and once you&#39;re in the routine of doing them they needn&#39;t be expensive.&amp;nbsp;&amp;nbsp; Be aware that large sample sets might improve the accuracy of your research, but they also slow you down.&amp;nbsp; Keep the groups small and focused - applicable to the size of team you have to analyse and react to the data. Done right, these groups can be used to continually scrutinise your system and uncover problems.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
Once those problems are found, the same evidence can be used to guide potential solutions. Produce some quick lo-fi designs, present them to another (or the same, if you are so inclined) small group and wireframe the best ones to include in your argument to proceed.&amp;nbsp; I honestly believe that once you&#39;re in the habit, this whole process can be implemented in two or three weeks.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
Having got the go ahead, you have a coherent picture of the problem and a solid starting point for you commence the full blown design work.&amp;nbsp; You can then move into a short, sharp and probably seriously intense design phase.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
At all points, the design that you&#39;re coming up with is, of course, important. However, it&#39;s vital that you don&#39;t underestimate the value of the thinking process that goes into the design. Keep earlier iterations of the design, keep notes on why the design changed. This forms a reference document that you can use to remind yourself of the reasoning behind your design. This needn&#39;t be a huge formal tome; it could be as simple as comments in your wireframes, but an aide&amp;nbsp;mémoire&amp;nbsp;for the rationale behind where you are today is important.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
In this short sharp design phase you need to make sure that you get to an initial conclusion quickly and that you bear in mind that this will almost certainly not be the design that you actually end up with.&amp;nbsp; This initial design is primarily used to illustrate the problem and the current thinking on the solution to the developers. It is absolutely not a final reference document.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
As soon as you become wedded to a design, you lose the ability to be agile. Almost by definition, an agile project will not deliver exactly the functionality it set out deliver. Recognise this and ensure that you do the level of design appropriate to bring the project to life and no more.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
When the development starts, the UX design work doesn&#39;t stop. This is where the ultimate design work begins - the point at which the two approaches start to meld.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
As the developers start to produce work, the UX expert starts to have the richest material he could have - a real system. It is quite amazing how quickly an agile project can produce a working system that you are able to put in front of users, and there&#39;s nothing quite like a real system for investigating system design.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
It&#39;s not that the wireframes are longer of use. In fact, early on the wireframes remain a vital, and probably only coherent view of the system and these should evolve as the project develops.&amp;nbsp; As elements in the system get built and more rigidly set the wireframes are updated to reflect them. As new problems and opportunities are discovered, the wireframes are used to explore them.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
This process moves along in parallel to the BA work that&#39;s taking place on the project. As the customer team splits and prioritises the work, the UX expert turns their attention to the detail of their immediate problems, hand in hand with the BAs. The design that&#39;s produced is then used to explain the proposed solutions to the development team and act as a useful piece of reference material.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
At this point the developers will often have strong opinions on the design of the solution, and these should obviously be heard. The advantage the design team now have is that they have a body of research and previous design directions to draw on, and a coherent complete picture against which these ideas (and often criticisms) can be scrutinised.&amp;nbsp; It&#39;s not that the design is complete, or final, it&#39;s that a valuable body of work has just been done, which can be drawn upon in order to produce the solution.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
As you get towards the end of the project, more and more of the wireframe represents the final product.&amp;nbsp; At this point functionality can be removed from the wireframe in line with what&#39;s expected to be built.&amp;nbsp; In fact, this is true all the way through the project, it&#39;s just that people become more acutely aware of it towards the end.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
This is a useful means of testing the minimum viable product. It allows you to check with the customer team how much can be taken away before you have a system that could not be released: a crucial tool in a truly agile project. &amp;nbsp;If you don&#39;t have the wireframes to show people, the description of functionality that&#39;s going to be in or out can be open to interpretation - which means it&#39;s open to misunderstanding.&lt;/div&gt;
&lt;h4&gt;
Conclusion&lt;/h4&gt;
&lt;div dir=&quot;ltr&quot;&gt;
It takes work to bring a UX expert into an agile project, and it takes awareness and honesty to ensure that you&#39;re not introducing a big-up-front design process that reduces your ability to react.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
However, by keeping in mind some core principles - that you need to be able to throw and willing to throw work away, you should not become wedded to a design early on, you listen to feedback and react, you keep your level of work and techniques fit for the just-in-time problem that you need to solve right now - you can add four huge advantages to your project.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;A coherent view and design that bind the disparate elements together into a complete system.&lt;/li&gt;
&lt;li&gt;Expert techniques and knowledge that allow you to discover the right problems to fix with greater accuracy.&lt;/li&gt;
&lt;li&gt;Design practices and investigative processes that allow you to test potential solutions earlier in the project (i.e.&amp;nbsp;with less cost) than would otherwise be possible, helping ensure you do the right things at the right time.&lt;/li&gt;
&lt;li&gt;Extremely expressive communication tools that allow you to describe the system you&#39;re going to deliver as that understanding changes through the project.&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
&lt;div dir=&quot;ltr&quot;&gt;
Do it right and you can do all this and still be agile.&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/2476745191778814466/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/10846234/2476745191778814466' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/2476745191778814466'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/2476745191778814466'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2013/08/agile-and-ux-can-mix.html' title='Agile and UX can mix'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJKWDfU-frt3gi_ubtgJ8msvcKcp6ChPvxN_uLMCR-FqygXX-IabdD7vmbQ3RHXnwvmQRPaiXacC6CoIfaYcyh0hGdV0PQzbjFaqf_ECJjuD2JzQVdrINY6YBtjgISt_4/s113/Rob+Baillie.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-2063991205169433030</id><published>2013-08-02T13:53:00.006+01:00</published><updated>2013-08-08T16:26:55.758+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="management"/><title type='text'>Remote workforces and the 12 golden questions</title><content type='html'>&lt;div dir=&quot;ltr&quot;&gt;
I had an interesting conversation with a friend the other day about the difficulties in managing a remote team. That is a team who aren&#39;t all located in the same office. Some may be home workers, some may work in different offices.&amp;nbsp; The main crux of the discussion was around how you turn a group of people into team, garner some emotional connection between them, and to you and your company, and then get the best out of them.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
After a few days of gestation and rumination it came to me. The rules are the same as with a local team - you may do different things and the problems may be more difficult to overcome, but the techniques you use are fundamentally the same.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
That thinking led me back to Marcus Buckingham&#39;s fantastic book &quot;First Break all the Rules&quot;. If you manage people and haven&#39;t read this book - shame on you. It is a must read.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
One of the main arguments in the book revolves around a set of questions you should ask of your staff defined by years of research by Gallup based on the strongest&amp;nbsp;signifiers&amp;nbsp;of a team that is performing well.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
If you get good responses to these questions then you probably have a good team.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
Now I&#39;m not going to explain the why&#39;s and wherefores of these questions, that has been done far better than I ever could in Marcus&#39;s book. Buy it and read it.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
What I&#39;d like to do is go over each of the questions and look at what you may need to do as a manager of a remote team in order to ensure that you get positive responses to these questions.&lt;/div&gt;
&lt;h3&gt;
I know what is expected of me at work.&lt;/h3&gt;
&lt;div dir=&quot;ltr&quot;&gt;
Much like you would with a locally grouped team this is as simple, and as difficult as it sounds: keeping in touch, setting targets and boundaries, being available and honest. All those things that a good manager instinctively does.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
The only real difference is that it takes more effort to organise those face-to-face chats.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
It starts with honesty at the interview: clearly defining the role that&#39;s on offer, what&#39;s involved and what&#39;s not involved. From there it moves to regular catch ups to get a feel for where they think they are, and for you to feed back where they actually are, then finally to ensuring that rewards and praise are given when the expectations are met and exceeded.&amp;nbsp; Put in the simplest of terms you&#39;re regularly telling them what you expect then reinforcing that with action.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
For some people this will feel like constant badgering, and for others you&#39;ll never be able to do enough, but I don&#39;t think there&#39;s anything about remote working that makes this fundamentally different to managing local workers.&lt;/div&gt;
&lt;h3&gt;
&lt;b&gt;I have the materials and equipment I need to do my work right.&lt;/b&gt;&lt;/h3&gt;
&lt;div dir=&quot;ltr&quot;&gt;
Every tool you would normally provide in an office you should expect to provide for a remote worker. OK, maybe not the pen and pad, but you could consider corporate branded versions of both. At least it&#39;s a reminder of who they work for!&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
Every bit of software you would normally provide on a desktop needs to be available in their home office.&amp;nbsp;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
Every document that they may need to access on the move should be available&amp;nbsp;on-line&amp;nbsp;&amp;nbsp; Workers that are expected to spend most their time on client sites should have access to software that is appropriate for onsite work from any device that has internet access.&amp;nbsp; Ideally they should have offline versions too. I.e. access to versions of their software that works when not connected to the internet, that will automatically sync when the connection is made available.&amp;nbsp; If you&#39;ve ever used gmail, blogger or evernote on a disconnected tablet you will know what I mean.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
You need to do everything you can to limit the chances that they&#39;ll ever be in a situation where they are disconnected from their tools.&lt;/div&gt;
&lt;h3&gt;
&lt;b&gt;At work I have the opportunity to do what I do best every day.&lt;/b&gt;&lt;/h3&gt;
&lt;div dir=&quot;ltr&quot;&gt;
You might hope that this should be easier to achieve with remote workers than it would with a team in a single office.&amp;nbsp; Working on the move or at home gives people a chance to get on and do some work with out all those pesky distractions like other people.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
However, its very easy to underestimate the impact remote working has on ease of communication, and in turn, the amount of time it takes to have those communications.&amp;nbsp; If you&#39;re not careful, those informal 2 minute chats in the kitchen turn into 1000 word project update documents.&amp;nbsp; You can see how there can be a death off a thousand cuts as layers of bureaucracy are added in order to keep everyone in the loop.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
In addition, how can a manager see what a team member is best at when they don&#39;t physically witness them doing it.&amp;nbsp; It&#39;s not always easy in the office to spot someone&#39;s talents (or areas of difficulty for that matter) and guide them towards utilising them.&amp;nbsp; It&#39;s an order of magnitude harder when you don&#39;t spend that vital face to face time with them every day.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
Ironically it can be tempting to have people fill in time-sheets and detailed updates in order to help spot the things that are done quickly and well, that are second nature, but then this simply distracts people from what they do best, and not everyone&#39;s talent is writing updates!&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
There&#39;s no simple answer to this. It takes a very special manager who can read their employees from a distance and a special kind of employee who is self aware enough to be honest about their strengths and weaknesses.&amp;nbsp; It starts with the culture of the management team and their all pervasive attitude towards spotting strengths.&amp;nbsp; They need to make sure that the workforce is constantly aware that this is the approach the management team is taking and that gives employees a strong incentive to be honest.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
Part of that is then listening to your staff when they describe areas if difficulties. Sometimes this may highlight personal areas where the talents are lacking, in others it may be that the processes are getting in the way of providing real value. In either case you need to clearly assess the situation and act decisively and positively when needed.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
It&#39;s vital that everybody is very clear about what they, and their team, do best and that people are allowed to focus on that as much as possible.&lt;/div&gt;
&lt;h3&gt;
&lt;b&gt;In the last 7 days I have received recognition or praise for doing good work.&lt;/b&gt;&lt;/h3&gt;
&lt;div dir=&quot;ltr&quot;&gt;
This one should be simple.&amp;nbsp; All you have to do is follow the same rules that you normally would in the office: praise publicly or privately depending on the person you&#39;re dealing with.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
Praise successes at the monthly get together, on the intranet, via mail, a conference call or a chat on the phone whichever is appropriate for the person and level of success.&amp;nbsp; However, whenever, just don&#39;t forget to do it.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
Of course, you have to be much more diligent about this since the people you&#39;re praising aren&#39;t in front if you all the time.&amp;nbsp; It&#39;s harder to spot their frustration and disenchantment when they&#39;re not getting the praise they feel they deserve - you can&#39;t see their face and their minute by minute attitude.&amp;nbsp; For this reason I&#39;d suggest that it&#39;s probably better to err on the side of too much praise than too little, and maybe even have a reminder in your calendar that pops up every couple of days so you don&#39;t forget.&lt;/div&gt;
&lt;h3&gt;
&lt;b&gt;My supervisor, or someone at work, seems to care about me as a person.&lt;/b&gt;&lt;/h3&gt;
&lt;div dir=&quot;ltr&quot;&gt;
The main thing is honesty, and if you can fake that you&#39;ve got it made...&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
In all seriousness though, you do actually need to care.&amp;nbsp; In order to care you need to connect with people.&amp;nbsp;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
You&#39;ll spot a repeating theme here, and at the risk of sounding like a broken record, you can only connect with people if you communicate with them, and with a remote workforce that takes a lot of effort.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
Whilst this point isn&#39;t just about the tough times, if you find someone&#39;s having a hard time then you need to break that remoteness, get yourself into their locale and meet up on neutral territory. Show that you care enough about them as a person that you&#39;ll take the time to go see them in their local café.&amp;nbsp; Show that its not all one way, that you&#39;ll make the effort.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
It&#39;s about making sure that your team know that it&#39;s not all about the work they need to do today, but it&#39;s about them as a human-being having a valued place in a team that supports each other.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
For some people it will be inappropriate to cross into the personal life, maybe they like working in a remote team &lt;i&gt;because &lt;/i&gt;of the fact that its remote. However, it can still be valuable for those people to know that you understand and respect that, rather than simply don&#39;t care about them.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
Even people who don&#39;t want regular catch ups want to be reminded that you know that and you&#39;re trying your best to act in line with their desires.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
You have to be extremely careful about crossing people&#39;s personal boundaries and invading into their personal space.&amp;nbsp; Be honest with yourself about that, and recognise that not everyone wants their boss to be their best friend and that for most people it would be extremely distressing if you turned up on their doorstep unannounced!&lt;/div&gt;
&lt;h3&gt;
&lt;b&gt;There is someone at work who encourages my development.&lt;/b&gt;&lt;/h3&gt;
&lt;div dir=&quot;ltr&quot;&gt;
When you&#39;re working remotely it can sometimes seem like you have nothing other than unrealistic demands, one after the other from a manager who can then veer wildly to forgetting you exist. This is what you need to try to overcome.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
There needs to be a tough combination of slack in the schedule, freedom to explore and encouragement to follow new paths.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;
If your team have no time to do anything other than the day&#39;s work then they have no opportunity to develop.&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
If they have plenty of time, but no contact then they&#39;ll feel you don&#39;t care about their development.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
You need to bring conversations on development to the front and ensure that they&#39;re had out loud.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
Ensure that you have a process in place to discuss the direction your staff want to move in and ensure that they have the support they need in order to take those steps.&amp;nbsp; This may involve having decent expenditure on training, on in house resources and applications, it may be as simple as just letting your staff have time to explore.&amp;nbsp; It certainly includes letting them fail from time to time and not being judgemental about the outcome.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
Not all this can be done remotely. It&#39;s tough to feel the support of someone that is not physically present, and &amp;nbsp;as with so many of these points you need to acknowledge that you&#39;re going to travel. You absolutely need some face to face time.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
It may be that you need to put a central training team together and fly, train or bus people in to get their training.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
You should!&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
It may end up being more expensive than it would have been to have a co-located office and training team, but that&#39;s the decision you took when you decided to employ a remote team.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
Good quality learning and development software can help, as can access to third-party on-line training catalogues and I imagine that there is a greater return on investment on these tools than there would be in a local office. &amp;nbsp;However, making courses available to people is not the same as encouraging and supporting them in their development.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
Consider mentoring programmes and ensure that you pay the expenses to get people together with their mentors.&amp;nbsp; Don&#39;t just assume that the mentors know what they&#39;re doing, put a mentoring &lt;i&gt;team&lt;/i&gt; together so that they can support each other, and ensure that you have a training budget to teaching people how to be a mentor.&amp;nbsp; Don&#39;t forget, being a mentor can be a great way to develop the mentor!&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
If you want your team to think you&#39;re serious about their development, you need to get serious about their development.&lt;/div&gt;
&lt;h3&gt;
&lt;b&gt;At work my opinions seem to count&lt;/b&gt;&lt;/h3&gt;
&lt;div dir=&quot;ltr&quot;&gt;
I&#39;d suggest that in order for a new team member to feel their opinions matter they first need to feel that their co-workers&#39; opinions matter.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
From that you can then gestate the idea that they are allowed to have opinions, leading to you following through on some of their thoughts and ideas so that they feel &lt;b&gt;their&lt;/b&gt; ideas matter.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
Simple eh?&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
At the core of it, as always is the need to communicate. Not just back to the team member with the big idea, or serious concern, but with the whole team.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
Regularly asking for feedback and opinions and then acting upon them. Becoming known as the manager that doesn&#39;t always assume that they know better.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
Technology can help with this.&amp;nbsp; Open forums with no moderation (unless it&#39;s absolutely necessary). Having everyone involved in it, from the CEO to the intern, and a culture of respect around the postings that means every question or idea is addressed with care and thought.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
That&#39;s not to say that every post is publicly stated as the best idea or most insightful question there has ever been, but that common courtesy and time is given in the response.&amp;nbsp; Most sane people have no problem being told they&#39;re wrong as long as it is clear and respectful and comes with an invitation for more.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
There is also the HR angle: that people need to be able to state when they think a co-worker is not up to scratch, behaving inappropriately or suchlike.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
Accessibility, openness and a visible commitment to acting on information is the only way to get this feeling fostered.&amp;nbsp; And guess what, it comes back again to two way communication.&lt;/div&gt;
&lt;h3&gt;
&lt;b&gt;The mission / purpose of my company make me feel my job is important.&lt;/b&gt;&lt;/h3&gt;
&lt;div dir=&quot;ltr&quot;&gt;
OK, so it can seem that there&#39;s very little you can do about this, either your company resonates with your employees or it doesn&#39;t. The reality is that you can affect this quite significantly.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
It&#39;s all too easy to recruit without your companies values in mind. And when I say values, I don&#39;t mean those in your company brochure, I mean those true values that actually drive the business.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
An estate agents is never going to be driven by anything other than selling or letting houses, and that&#39;s the way it should be. There are different ways in which you a company may approach that, but the core value is one that selling houses is a good thing, and that you&#39;ll make money out of it.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
Put simply, if you&#39;re an estate agent and you hire someone who thinks that a buoyant housing market, the need for a &#39;property ladder&#39;, low interest rates, and easy access to credit is a bad thing then you&#39;ve hired someone who will never feel their job is important.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
Consider that in your recruitment process.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
I&#39;m not saying that you can&#39;t , or shouldn&#39;t have a business with a mix of opinions, merely that you should honestly recognise the limitations of internal corporate marketing.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
Having said that, you do need to market the business internally. You still need to remind people why they are here, and why the company is doing what it&#39;s doing. If you don&#39;t define the culture of the business then individuals will impose a culture upon it and it may not be the one you want. An outgoing but negative employee can very easily, and often quite unintentionally impose a negative culture on the whole of a department.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
As with so many of these topics, communication is the key, more so with a remote workforce than at any other time.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
Let the team know what the company feels is important, and make sure you don&#39;t stray too far from the credible truth or your employees will start to think you stand for lies.&lt;/div&gt;
&lt;h3&gt;
&lt;b&gt;My co-workers are committed to doing quality work.&lt;/b&gt;&lt;/h3&gt;
&lt;div dir=&quot;ltr&quot;&gt;
There are three significant risks with a remote workforce that can put this into jeopardy.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
First - it can be difficult to spot when you have a member of the team that&#39;s not committed to quality work.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;
Second - it can be difficult to sot someone who thinks their team-mates are not committed to quality work.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;
Third - it can be difficult to ensure that everyone knows what quality work their team-mates are doing.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
With many of the other points the focus is on communication in order to feedback on progress both up and down the chain of command. This is much more focused on the sideways communication.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
At the simplest level this is about regular cross team updates where you ensure that everyone knows what&#39;s going on in the whole team, particularly highlighting points of note.&amp;nbsp; This directly addresses the third risk, but doesn&#39;t deal with the other two.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
You need to follow it up by fostering an environment where feedback on peers is taken seriously.&amp;nbsp; You need to ensure that your team feel comfortable asking about their team mates&#39; progress, or pointing out areas of concern or difficulty.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
This involves giving an honest and clear response.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
If you feel the comments are unjustified you need to be able to clearly state why, but still then ensure you take the comments on board and react to them. Recognise that they may know more about the situation than you do.&amp;nbsp; You need to give that dual impression - you value feedback, and that you value your staff - you&#39;ll hear criticism and concern and act to rectify issues, but you&#39;ll defend and protect when it is unjustified.&lt;/div&gt;
&lt;h3&gt;
&lt;b&gt;I have a good friend at work?&lt;/b&gt;&lt;/h3&gt;
&lt;div dir=&quot;ltr&quot;&gt;
Obviously a collection of remote workers have far fewer chances to socialise than those working together in an office.&amp;nbsp; They&#39;ll never just decide to go to the pub on a Wednesday evening and never naturally make those odd cross department smoking cliques, nor football ones neither - all simply because they&#39;re not at the office.&amp;nbsp; This means they are far less likely to make the same kinds of personal connections than they would otherwise.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
The problem and potential solutions are fairly clear but easy to overlook.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
You absolutely have to have a higher than usual entertainments budget. You have to meet up at least every month in order for those face to face relationships to blossom. But it&#39;s more than that. You have to foster an environment where building &lt;i&gt;remote&lt;/i&gt;&lt;i&gt; &lt;/i&gt;relationships is also the norm. You have to provide virtual replacements for the Wednesday evening pub and smoker&#39;s corner.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
For example, your management team must have a relaxed attitude when communicating via mail.&amp;nbsp; It has to be clear that email system is more than just a business tool, that it can be a social one too. You have to make an effort to build an environment in which social networks will blossom.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
Consider tools like Yammer (corporate social networking site) and then push the management to actually use them, for a combination of business and social reasons.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
Provide the mechanism to allow for the hosting of virtual book clubs, badminton ladders and a Modern Warfare 3 clan.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
Recognise the kinds of people you have employed and ensure that they have a means of accessing people at work who are like minded and then make it feel normal that they will reach out and find each other.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
What offices you do have, don&#39;t be afraid to add a big chill out area and kitchen so that when people &lt;i&gt;are&lt;/i&gt; in the office they get that reinforcement - &quot;this is a company where we actively encourage you to be friends&quot;&lt;/div&gt;
&lt;h3&gt;
&lt;b&gt;In the last six months someone at work has talked to me about my progress.&lt;/b&gt;&lt;/h3&gt;
&lt;div dir=&quot;ltr&quot;&gt;
There is no reason why this should be difficult. Organise regular meetings, on-line or otherwise, to discuss progress. Have a solid process in place that can flex for individual needs.&amp;nbsp; All the things you would normally do.&amp;nbsp; Every six months is a bare minimum, every two is OK, once a month is ideal - as a general rule.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
I could labour the point, but I think most of what needs to be said has been said already!&lt;/div&gt;
&lt;h3&gt;
&lt;b&gt;This last year I have had opportunities at work to learn and grow.&lt;/b&gt;&lt;/h3&gt;
&lt;div dir=&quot;ltr&quot;&gt;
It can be very tempting to feel that your home workers are sitting at home happy in the knowledge that they&#39;re doing a good job and have a great work home life balance. Maybe that&#39;s true. Maybe all they want is to get their job done and then play in the garden with their kids.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
Maybe.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
However, just because they&#39;re remote doesn&#39;t mean they&#39;re not ambitious.&amp;nbsp; I don&#39;t think there&#39;s any reason why a home worker will be any less likely than an office one to want to progress, either in their career, or personally.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
Also, not every remote worker is a home worker. &lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
Those team members that are sitting at a desk 50 miles away, out of sight, are more able to look for opportunities outside of your company than someone that&#39;s sat 5 metres away.&amp;nbsp; Take their progress as seriously as you would any other staff member&#39;s.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
Catch up regularly to learn about their goals and then do what you can to help them reach the realistic ones, learn about their career concerns and do what you can to help them overcome them, or to placate them.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
Tailor your roles to suit the talents and desires of your team members and make sure you give the ones who need, deserve and are up to it the opportunity to stretch themselves in new directions.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
If you don&#39;t give your team members the encouragement and opportunity to develop then they&#39;ll find the opportunities through a new role in a new company, and just like your local workers, you&#39;ll have no idea it&#39;s going to happen until it&#39;s too late.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
So, do more than you think you need to!&lt;/div&gt;
&lt;h3&gt;
&lt;b&gt;Conclusion&lt;/b&gt;&lt;/h3&gt;
&lt;div dir=&quot;ltr&quot;&gt;
Good management is good management, regardless of how local or remote the team is, and good management takes effort.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
The truth of the matter is that with a remote workforce that effort is increased.&amp;nbsp; You need to be more astute, more available and more willing to put the effort in than if your team is sat next to you.&amp;nbsp; You lose so many of the visual and social clues that a good manager uses every day to gauge the health of its team that you need to compensate in many other areas.&amp;nbsp; You also have to acknowledge that you&#39;re not likely to be as effective, it simply isn&#39;t possible.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
You need to get imaginative about how you remain in contact, how you foster a team spirit and an emotional connection.&amp;nbsp; Technology plays a part, of course it does. Good collaboration tools with social media aspects make it possible to create social groups within your company and allow those people to seek out like minded individuals in a way that simply wasn&#39;t possible, or necessary, 10 years ago.&amp;nbsp; However, the technology isn&#39;t a panacea. You still need to create an environment in which people actually want to connect.&amp;nbsp; Without the right cultural context, you&#39;ll simply have a dead application&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
Still, the rules are simple and the techniques familiar.&amp;nbsp; There&#39;s nothing fundamentally different about managing a remote team, you&#39;re still dealing with people, after all.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
If you honestly care about your role as a manager, a need to create a team that performs and are willing and able to put the time in, then you probably won&#39;t go far wrong.&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/2063991205169433030/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/10846234/2063991205169433030' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/2063991205169433030'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/2063991205169433030'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2013/08/remote-workforces-and-12-golden.html' title='Remote workforces and the 12 golden questions'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJKWDfU-frt3gi_ubtgJ8msvcKcp6ChPvxN_uLMCR-FqygXX-IabdD7vmbQ3RHXnwvmQRPaiXacC6CoIfaYcyh0hGdV0PQzbjFaqf_ECJjuD2JzQVdrINY6YBtjgISt_4/s113/Rob+Baillie.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-7913220068837386735</id><published>2013-06-09T14:30:00.002+01:00</published><updated>2013-08-08T16:27:53.900+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="estimation"/><category scheme="http://www.blogger.com/atom/ns#" term="management"/><category scheme="http://www.blogger.com/atom/ns#" term="planning"/><category scheme="http://www.blogger.com/atom/ns#" term="processes"/><title type='text'>Measuring the time left</title><content type='html'>Burn-down (and burn-up, for that matter) charts are great for those that are inclined to read them, but some people don&#39;t want to have to interpret a pretty graph, they just want a simple answer to the question &quot;How much will it cost?&quot;&lt;br /&gt;
&lt;br /&gt;
That is if, like me, you work in what might be termed a semi-agile&lt;span style=&quot;font-size: xx-small;&quot;&gt;*1&lt;/span&gt; arena then you also need some hard and fast numbers. What I am going to talk about is a method for working out the development time left on a project that I find to be pretty accurate. I&#39;m sure that there are areas that can be finessed, but this is a simple calculation that we perform every few days that gives us a good idea of where we are.&lt;br /&gt;
&lt;h3&gt;
The basis.&lt;/h3&gt;
It starts with certain assumptions:&lt;br /&gt;
&lt;h4&gt;
&lt;b&gt;You are using stories.&lt;/b&gt;&lt;/h4&gt;
OK, so they don&#39;t actually have to be  called stories, but you need to have split the planned functionality into small chunks of manageable and reasonably like sized work.&lt;br /&gt;
Having done that you need to have a practice of working on each chunk until its finished before moving on to the next, and have a customer team test and accept or sign off that work soon after the developers have built it.&lt;br /&gt;
You need that so that you uncover your bugs, or unknown work as early as possible, so you can account for them in your numbers.
&lt;br /&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;h4&gt;
&lt;b&gt;Your customer team is used to writing stories of the same size.&lt;/b&gt;&lt;/h4&gt;
When your customer team add stories to the mix you can be confident that you won&#39;t &lt;i&gt;always&lt;/i&gt;&lt;i&gt; &lt;/i&gt;have to split them into smaller stories before you estimate and start working on them.&lt;br /&gt;
This is so you can use some simple rules for guessing the size of the work that your customer team has added but your developers have not yet estimated.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;h4&gt;
You estimate using a numeric value.&lt;/h4&gt;
It doesn&#39;t matter if you use days work, story points or function points, as long as it is expressed as a number, and that something estimated to take 2 of your unit is expected to take the same as 2 things estimated at 1.&lt;br /&gt;
If you don&#39;t have this then you cant do any simple mathematics on the numbers you have and it&#39;ll make your life much harder.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;h4&gt;
&lt;b&gt;Your developers quickly estimate the bulk of the work before anything is started.&lt;/b&gt;&lt;/h4&gt;
This is not to say that the whole project has a Gandalf like startup: &quot;Until there is a detailed estimate, YOU SHALL NOT PASS&quot;; rather that you T-shirt cost, or similar, most of your stories so that you have some idea of the overall cost of the work you&#39;re planning.&lt;br /&gt;
You need this early in the project so that you have a reasonable amount of data to work with&lt;br /&gt;
&lt;h4&gt;
&lt;b&gt;Your developers produce consistent estimates.&lt;/b&gt;&lt;/h4&gt;
&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
Not that your developers produce accurate estimates, but that they tend to be consistent; if one story is underestimated, then the next one is likely to be.&lt;br /&gt;
This tends to be the case if the same group of developers estimate all the stories that they all involve making changes to the same system. If a project involves multiple teams or systems then you may want to split them into sub projects for the means of this calculation.&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;h4&gt;
&lt;b&gt;You keep track of time spent on your project.&lt;/b&gt;&lt;/h4&gt;
Seriously, you do this right?&lt;br /&gt;
It doesn&#39;t need to be a detailed analysis of what time is spent doing what, but a simple total of how much time has been spent by the developers, split between the time spent on stories and that on fixing defects.&lt;br /&gt;
If you don&#39;t do this, even on the most agile of projects, then your bosses and customer team don&#39;t have the real data that they need to make the right decisions.&lt;br /&gt;
You, and they, are walking a fine line to negligent&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
If you have all these bits then you&#39;ve got something that you can work with...&lt;br /&gt;
&lt;h3&gt;
The calculation.&lt;/h3&gt;
The calculation is simple, and based on the following premises:&lt;br /&gt;
&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;If your previous estimates were out, they will continue to be out by the same amount for the whole of the project.&lt;/li&gt;
&lt;li&gt;The level of defects created by the developers and found by the customer team will remain constant through the whole project.&lt;/li&gt;
&lt;li&gt;Defects need to be accounted for in the time remaining.&lt;/li&gt;
&lt;li&gt;Un-estimated stories will be of a similar size to previously completed work.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
The initial variables:&lt;/h4&gt;
&lt;pre&gt;
totalTimeSpent = The total time spent on all development work (including defects).

totalTimeSpentOnDefects = The total time spent by developers investigating and fixing defects.

numberOfStoriesCompleted = The count of the number of stories that the development team have completed and released to the customer.

storiesCompletedEstimate = The sum of the original estimates against the stories that have been completed and released to the customer.

totalEstimatedWork = The sum of the developers&#39; estimates against stories and defects that are yet to do.

numberOfStoriesCompleted = The count of  number of a stories that have been completed by the development team and released to the customer.

numberOfUnEstimatedStories = The count of the number of stories that have been raised by the customer but not yet estimated by the development team.

numberOfUnEstimatedDefects = The count of the number of defects that have been found by the customer but not yet estimated by the development team.
&lt;/pre&gt;

Using these we can work out:&lt;br /&gt;
&lt;h4&gt;
Time remaining on work that has been estimated by the development team.&lt;/h4&gt;
For this we use a simple calculation on the previous accuracy of the estimates.&lt;br /&gt;
This includes taking into account the defects that will be found, and need to be fixed against the new feunctionality that will be built.&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;
estimateAccuracy = totalTimeSpent / storiesCompletedEstimate

predictedTimeRemainingOnEstimatedWork = ( totalEstimatedWork * estimateAccuracy )
&lt;/pre&gt;
&lt;h4&gt;
Time remaining on work that has not been estimated by the development team.&lt;/h4&gt;
In order to calculate this, we rely on the assumptions that the customer team have got used to writing stories of about the same size every time.&lt;br /&gt;
You may need to get a couple of developers to help with this by splitting things up with the customer team as they are creating them. I&#39;d be wary of getting then to estimate work though.&lt;br /&gt;
&lt;pre&gt;
averageStoryCost = totalTimeSpent / numberOfStoriesCompleted

predictedTimeRemainingOnUnEstimatedStories = numberOfUnEstimatedStories * averageStoryCost


averageDefectCost = totalTimeSpentOnDefects / numberOfStoriesCompleted

predictedTimeRemainingOnUnEstimatedDefects = numberOfUnEstimatedDefects * averageDefectCost&amp;nbsp;
&lt;/pre&gt;
&lt;h4&gt;
Total predicted time remaining&lt;/h4&gt;
The remaining calculation is then simple, it&#39;s the sum of the above parts.&lt;br /&gt;
We&#39;ve assessed the accuracy of previous estimates, put in an allocation for bugs not yet found, and assigned a best guess estimate against things the development team haven&#39;t yet put their own estimate.&lt;br /&gt;

&lt;pre&gt;
totalPredictedTimeRemaining = predictedTimeRemainingOnEstimatedWork + predictedTimeRemainingOnUnEstimatedStories + predictedTimeRemainingOnUnEstimatedDefects&amp;nbsp;
&lt;/pre&gt;
&lt;h3&gt;
The limitations&lt;/h3&gt;
I find this calculation works well, as long as you understand its limitations.&lt;br /&gt;
I hope to present some data in this blog very soon, as we already have some empirical evidence that it works.&lt;br /&gt;
Admittedly, for the first 20% or so of the project the numbers coming out of thus will fluctuate quite a bit. This is because there isn&#39;t enough &#39;yesterday&#39;s weather&#39; data to make it the estimate accuracy calculation meaningful. The odd unexpectedly easy (or hard) story can have a bit effect on the numbers.&lt;br /&gt;
Also, if your testing and accepting of stories lags far behind your development or if you don&#39;t fix your bugs first, you will under estimate the number of bugs in the system.
However, if you know these things you can react to them as you go along.&lt;br /&gt;
&lt;h3&gt;
Further Work&lt;/h3&gt;
I am not particularly inclined to make changes to this calculation, as the assumptions and limitations are perfectly appropriate for the teams that I work with. For other teams this may not be the case, and I might suggest some slight alterations if you think they&#39;d work for you.&lt;br /&gt;
&lt;h4&gt;
Estimating number of defects not yet found.&lt;/h4&gt;
&lt;div&gt;
It seems reasonable for you to look at the average number of defects raised per story accepted and use this to work out the number of defects that have not yet been found. &amp;nbsp;These could then be included in your calculation based on the average cost of defects that you&#39;ve already fixed.&lt;/div&gt;
&lt;div&gt;
This might be a good idea if you have a high level of defects being raised in your team. &amp;nbsp;I&#39;d say high as meaning anything over about 20% of your time being spent fixing defects.&lt;/div&gt;
&lt;h4&gt;
Using the estimate accuracy of previous projects at the start of the new.&lt;/h4&gt;
&lt;div&gt;
As I pointed out earlier, a limitation of this method is the fact that you have limited information at the start of the project and so you can&#39;t rely on the numbers being generated for some time. &amp;nbsp;A way of mitigating this is to assume that this project will go much like the previous one.&lt;/div&gt;
&lt;div&gt;
You can then use the estimate accuracy (and defect rate, if you calculated one) from your previous project in order to mitigate the lack of information in this.&lt;/div&gt;
&lt;div&gt;
If you&#39;re using the same development team and changing the same (or fundamentally similar) applications, then this seems entirely appropriate.&lt;/div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;span style=&quot;font-size: xx-small;&quot;&gt;*1&lt;/span&gt; Semi-agile: I&#39;d define this is where the development of software is performed in a full agile manner, but the senior decision makers still rely on business case documentation, project managers and meeting once a month for updates.&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/7913220068837386735/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/10846234/7913220068837386735' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/7913220068837386735'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/7913220068837386735'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2013/06/measuring-time-left.html' title='Measuring the time left'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJKWDfU-frt3gi_ubtgJ8msvcKcp6ChPvxN_uLMCR-FqygXX-IabdD7vmbQ3RHXnwvmQRPaiXacC6CoIfaYcyh0hGdV0PQzbjFaqf_ECJjuD2JzQVdrINY6YBtjgISt_4/s113/Rob+Baillie.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-984883163840075002</id><published>2010-05-17T08:47:00.003+01:00</published><updated>2013-08-08T16:27:53.901+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="development"/><category scheme="http://www.blogger.com/atom/ns#" term="elegent"/><category scheme="http://www.blogger.com/atom/ns#" term="showing off"/><title type='text'>Pleasing line</title><content type='html'>Gotta admit, I&#39;m quite pleased with this line from my new ORM object based database connection library...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;$oFilter = Filter::attribute(&#39;player_id&#39;)-&gt;isEqualTo(&#39;1&#39;)-&gt;andAttribute(&#39;fixture_id&#39;)-&gt;isEqualTo(&#39;2&#39;);&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/984883163840075002/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/10846234/984883163840075002' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/984883163840075002'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/984883163840075002'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2010/05/pleasing-line.html' title='Pleasing line'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJKWDfU-frt3gi_ubtgJ8msvcKcp6ChPvxN_uLMCR-FqygXX-IabdD7vmbQ3RHXnwvmQRPaiXacC6CoIfaYcyh0hGdV0PQzbjFaqf_ECJjuD2JzQVdrINY6YBtjgISt_4/s113/Rob+Baillie.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-3871277377538074584</id><published>2008-06-23T10:01:00.003+01:00</published><updated>2013-08-08T16:27:53.893+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="management"/><category scheme="http://www.blogger.com/atom/ns#" term="processes"/><title type='text'>The Happiness Meter</title><content type='html'>As part of any iteration review / planning meeting there should be a section where everybody involved talks about how they felt the last iteration went, what they thought stood in the way, what they though went particularly well and suchlike.&lt;br /&gt;&lt;br /&gt;We find that as the project goes on, and the team gets more and more used to each other, this tends to pretty much always dissolve into everyone going &quot;alright I suppose&quot;, &quot;yeah fine&quot;.&lt;br /&gt;&lt;br /&gt;Obviously, this isn&#39;t ideal and will tend to mean that you only uncover problems in the project when they&#39;ve got pretty serious and nerves are pretty frayed.&lt;br /&gt;&lt;br /&gt;This is where &quot;The Happiness Meter&quot; comes in.&lt;br /&gt;&lt;br /&gt;Instead of asking the team if they think things are going OK and having most people respond non-committally, ask people to put a value against how happy they are with the last iteration&#39;s progress.  Any range of values is fine, just as long as it has enough levels in it to track subtle movements. I&#39;d go with 1-10.&lt;br /&gt;&lt;br /&gt;You don&#39;t need strict definitions for each level, it&#39;s enough to say &#39;1 is completely unacceptable, 5 is kinda OK, 10 is absolute perfection&#39;.&lt;br /&gt;&lt;br /&gt;At some point in the meeting, everyone in the team declares their level of happiness.  When I say everyone, I mean everyone: developers, customers, XP coaches, infrastructure guys, project managers, technical authors, absolutely everyone who is valuable enough to have at the iteration review meeting should get a say.&lt;br /&gt;&lt;br /&gt;In order to ensure that everyone gets to provide their own thought, each person writes down their number and everyone presents it at the same time.  The numbers are then taken recorded and a graph is drawn.&lt;br /&gt;&lt;br /&gt;From the graph we should be able to see:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;The overall level of happiness at the progress of the project.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;If there is any splits / factions in the interpretation of the progress.&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgpXLqOb2Gfl1SCv_X7H3TIfUyxenM4rirchTD6TFfiscRk0JyaNy0FZEoIbjkzb8vQZqsEmKOhEbCH9uBFP9vBL0ivYqd_xlU0dN5Oh3AQoxwbx862Z3p_r3HmUnv8xaf3FZ2Nmw/s1600-h/HappinessMeter.gif&quot;&gt;&lt;img style=&quot;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgpXLqOb2Gfl1SCv_X7H3TIfUyxenM4rirchTD6TFfiscRk0JyaNy0FZEoIbjkzb8vQZqsEmKOhEbCH9uBFP9vBL0ivYqd_xlU0dN5Oh3AQoxwbx862Z3p_r3HmUnv8xaf3FZ2Nmw/s320/HappinessMeter.gif&quot; border=&quot;0&quot; alt=&quot;&quot;id=&quot;BLOGGER_PHOTO_ID_5215000691724560498&quot; /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;If the level of happiness is low, this should be investigated; if there are any splits, this should be investigated; and just as importantly - if there are any highs, this should be investigated.  It&#39;s good to know why things go well so you can duplicate it over the next iteration.&lt;br /&gt;&lt;br /&gt;Factions tend to indicate that one part of the team has more power than the rest and the project is skewed into their interests rather than those of the team as a whole.&lt;br /&gt;&lt;br /&gt;You may want to split the graph into different teams (customer / developer) if you felt that was important, but I like to think of us all as one team on the same side...&lt;br /&gt;&lt;br /&gt;All said and done, the graph isn&#39;t the important bit - the discussion that comes after the ballot is the crucial aspect.  This should be a mechanism for getting people to talk openly about the progress of the project.&lt;br /&gt;&lt;br /&gt;UPDATE: Someone at work suggested a new name that I thought I should share: The Happy-O-Meter.</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/3871277377538074584/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/10846234/3871277377538074584' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/3871277377538074584'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/3871277377538074584'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2008/06/happiness-meter.html' title='The Happiness Meter'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJKWDfU-frt3gi_ubtgJ8msvcKcp6ChPvxN_uLMCR-FqygXX-IabdD7vmbQ3RHXnwvmQRPaiXacC6CoIfaYcyh0hGdV0PQzbjFaqf_ECJjuD2JzQVdrINY6YBtjgISt_4/s113/Rob+Baillie.png'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgpXLqOb2Gfl1SCv_X7H3TIfUyxenM4rirchTD6TFfiscRk0JyaNy0FZEoIbjkzb8vQZqsEmKOhEbCH9uBFP9vBL0ivYqd_xlU0dN5Oh3AQoxwbx862Z3p_r3HmUnv8xaf3FZ2Nmw/s72-c/HappinessMeter.gif" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-6215957321582561979</id><published>2008-06-21T10:49:00.003+01:00</published><updated>2013-08-08T16:27:53.898+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="management"/><category scheme="http://www.blogger.com/atom/ns#" term="processes"/><title type='text'>Ideas for improving innovation and creativity in an IS department</title><content type='html'>At our work we&#39;ve set up a few &#39;action teams&#39; to try to improve particular aspects of our working environment.&lt;br /&gt;&lt;br /&gt;The team that I&#39;m a member of is responsible for &#39;Innovation and Creativity&#39;.&lt;br /&gt;&lt;br /&gt;We&#39;re tasked with answering the question &quot;How do we improve innovation and creativity in IS?&quot; - How we can foster an environment that encourages innovation rather than stifles it.&lt;br /&gt;&lt;br /&gt;As a bit of a background, the company is a a medium sized (2,500 plus employees) based mainly in the UK, but recently spreading through the world, the vast majority of whom are not IS based.  The IS department is about 100 strong and includes a development team of 25 people.  It&#39;s an SME at the point where it&#39;s starting to break into the big-time and recognises that it needs to refine its working practices a little in order to keep up with the pace of expansion.&lt;br /&gt;&lt;br /&gt;We met early last week and have put together a proposal to be taken to the senior management tier.  I get a feeling it will be implemented since our team included the IS Director (you don&#39;t get any senior in our department), but you never know what&#39;ll happen.&lt;br /&gt;&lt;br /&gt;I figured it might be interesting to record my understanding of the plan as it stands now, and then take another look in 6 months time to see what&#39;s happened to it...&lt;br /&gt;&lt;br /&gt;We decided that in order to have an environment that fosters creativity and innovation you need:&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Freedom:&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Time for ideas for form, for you to explore them, and then to put them into practice.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Stimulus:&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Outside influences that that can help to spark those ideas off - this may be from outside the organisation, or through cross-pollination within it.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Courage:&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;The conviction to try things, to allow them to fail or succeed on their own merit - both on the part of the individual and the organisation as a whole.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Natural Selection:&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;The need to recognise success when it happens, to take it into the normal operation of the business and make it work in practice.  Also, the need to recognise failure when it happens, and stop that from going into (or continuing to exist within) the team.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Recognition:&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;When we have a good idea, the people involved need to be celebrated.  When we have a bad idea, the people involved DO NOT need to be ridiculed.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Refinement:&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;The initial ideas aren&#39;t always the ones that are successful, it&#39;s the 4th, 5th or 125th refinement of that idea that forms the breakthrough.  We need to understand what we&#39;ve tried, and recognise how and why each idea has failed or succeeded so we can learn from that.&lt;br /&gt;&lt;br /&gt;&lt;hr/&gt;&lt;br /&gt;&lt;br /&gt;We put together some concrete ideas on how we&#39;re going to help put these in place - and bear in mind that this isn&#39;t just for the development team, this is for the whole of the IS department - development, project management, infrastructure, operations, service-desk, even the technology procurement...&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Curriculum:&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;A position set up that will be responsible for defining / tracking a curriculum for each job role in the department.&lt;br /&gt;&lt;br /&gt;Obviously this will be fed by those people that currently fulfil the roles, and will involve things ranging from ensuring the process documentation is up to scratch, through specifying reading lists (and organising the purchasing of the books for the staff) and suggesting / collecting / booking conferences, training courses and the like that might be of use.&lt;br /&gt;&lt;br /&gt;This takes the burden of responsibility away from the staff and managers - all you need is the idea and someone else will organise it and ensure it&#39;s on the curriculum for everyone else to follow up.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;IdeaSpace (TM ;-) ):&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;A forum for the discussion of ideas, and collection of any documentation produced on those ideas and their investigation.  This will (hopefully) form a library of past investigations as well as a stimulus for future ones.  Everyone in the department will be subscribed to it.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Lab days:&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Every employee is entitled to 2 days a month outside of their normal job to explore some idea they might have.  That time can be sandbagged to a point, although you can&#39;t take more than 4 days in one stint.  Managers have to approve the time in the lab (so that it can be planned into existing projects) and can defer the time to some extent, but if requests are forthcoming they have to allow at least 5 days each rolling quarter so that the time can&#39;t be deferred indefinitely.&lt;br /&gt;&lt;br /&gt;Whilst the exact format of the lab is yet to be decided, we&#39;re aiming to provide space away from the normal desks so that their is a clear separation from the day job and lab time.  People will be encouraged to take time in the lab as a team as well as individually.  Also, if we go into the lab for 3 days to find that an idea doesn&#39;t work, that idea should still be documented and the lab time regarded as a success (we learnt something)&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Dragon&#39;s Den:&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Gotta admit, I&#39;m not sure about some of the connotations of this - but the basic idea is sound.  Coming out of time in the Lab should be a discussion with peers about the conclusion of the investigation in a Dragon&#39;s Den format.  This allows the wider community to discuss the suitability of the idea for future investigations, or even immediate applicability.  One output of this meeting may be the formalisation of conclusions in the IdeaSpace.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Press Releases:&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;The company is already pretty good at this, but when something changes for the better we will ensure that we celebrate those changes and, even for a day, put some people up on pedestals.&lt;br /&gt;&lt;br /&gt;None of the above should be seen as a replacement for just trying things in our day to day job - but the idea is that these things should help stress to the department that change and progress are important aspects of what we do, and that we value it enough to provide a structure in which big ideas can been allowed to gestate.  Cross pollination and communication should just form part of our normal day job anyway, and we should ensure that our project teams are cohesive and communicate freely amongst and between themselves.&lt;br /&gt;&lt;br /&gt;Also, an important factor in the success of the above has to be the format of the Dragon&#39;s Den - if it is in any way imposing or nerve-racking then the idea is doomed to failure. As soon as people feel under pressure to justify themselves then the freedom disappears.&lt;br /&gt;&lt;br /&gt;I&#39;m quite excited by the prospect of putting these ideas into practice, and I wonder exactly where we&#39;ll end up.&lt;br /&gt;&lt;br /&gt;I&#39;ll keep you all posted.</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/6215957321582561979/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/10846234/6215957321582561979' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/6215957321582561979'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/6215957321582561979'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2008/06/ideas-for-improving-innovation-and.html' title='Ideas for improving innovation and creativity in an IS department'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJKWDfU-frt3gi_ubtgJ8msvcKcp6ChPvxN_uLMCR-FqygXX-IabdD7vmbQ3RHXnwvmQRPaiXacC6CoIfaYcyh0hGdV0PQzbjFaqf_ECJjuD2JzQVdrINY6YBtjgISt_4/s113/Rob+Baillie.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-5651099907874377705</id><published>2008-03-29T12:35:00.003+00:00</published><updated>2008-04-22T09:17:09.839+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Principles"/><title type='text'>Things I believe in</title><content type='html'>&lt;ul&gt;&lt;li&gt;It&#39;s easier to re-build a system from its tests than to re-build the tests from their system.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;You can measure code complexity, adherence to standards and test coverage; you can&#39;t measure quality of design.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Formal and flexible are not mutually exclusive.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The tests should pass, first time, every time (unless you&#39;re changing them or the code).&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Flexing your Right BICEP is a sure-fire way to quality tests.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Test code is production code and it deserves the same level of care.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Prototypes should always be thrown away.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Documentation is good, self documenting code is better, code that doesn&#39;t need documentation is best.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;If you&#39;re getting bogged down in the process then the process is wrong.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Agility without structure is just hacking.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Pairing allows good practices to spread.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Pairing allows bad practices to spread.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Cycling the pairs every day is hard work.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Team leaders should be inside the team, not outside it.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Project Managers are there to facilitate the practice of developing software, not to control it.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Your customers are not idiots; they always know their business far better than you ever will.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;A long list of referrals for a piece of software does not increase the chances of it being right for you, and shouldn&#39;t be considered when evaluating it.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;You can&#39;t solve a problem until you know what the problem is.  You can&#39;t answer a question until the question&#39;s been asked.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Software development is not complex by accident, it&#39;s complex by essence.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Always is never right, and never is always wrong.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Interesting is not the same as useful.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Clever is not the same as right.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The simplest thing that will work is not always the same as the easiest thing that will work.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;It&#39;s easier to make readable code correct than it is to make clever code readable.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;If you can&#39;t read your tests, then you can&#39;t read your documentation.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;There&#39;s no better specification document than the customer&#39;s voice.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;You can&#39;t make your brain bigger, so make your code simpler.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Sometimes multiple exit points are OK.  The same is not true of multiple entry points.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Collective responsibility means that everyone involved is individually responsible for everything.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Sometimes it&#39;s complex because it needs to be; but you should never be afraid to check.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;If every time you step forward you get shot down you&#39;re fighting for the wrong army.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;If you&#39;re always learning you&#39;re never bored.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;There are no such things as &quot;Best Practices&quot;.  Every practice can be improved upon.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Nothing is exempt from testing.  Not even database upgrades.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;It&#39;s not enough to collect data, you need to analyse, understand and act upon that data once you have it.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;A long code freeze means a broken process.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;A test hasn&#39;t passed until it has failed.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;If you give someone a job, you can&#39;t guarantee they&#39;ll do it well; If you give someone two jobs you can guarantee they&#39;ll do both badly&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Every meeting should start with a statement on its purpose and context, even if everyone in the meeting already knows.&lt;/li&gt;&lt;/ul&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/5651099907874377705/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/10846234/5651099907874377705' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/5651099907874377705'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/5651099907874377705'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2008/03/things-i-believe-in.html' title='Things I believe in'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJKWDfU-frt3gi_ubtgJ8msvcKcp6ChPvxN_uLMCR-FqygXX-IabdD7vmbQ3RHXnwvmQRPaiXacC6CoIfaYcyh0hGdV0PQzbjFaqf_ECJjuD2JzQVdrINY6YBtjgISt_4/s113/Rob+Baillie.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-3527314385605883422</id><published>2008-03-25T17:54:00.006+00:00</published><updated>2013-08-08T16:27:53.890+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="management"/><title type='text'>A reading list for our developers</title><content type='html'>An idea I&#39;m thinking of trying to get implemented at our place is a required reading list for all our developers.  A collection of books that will improve the way that developers think about their code, and they ways in which they solve problems.  The company would buy the books as gifts to the employees, maybe one or two every three months.&lt;br /&gt;&lt;br /&gt;Some questions though:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Is it fair for a company to expect its employees to read educational material out of hours?&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Conversely:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Is it fair for an employee to expect to be moved forward in their career without a little bit of personal development outside the office?&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;If anyone has any books out there that they&#39;d recommend - please let me know.  Otherwise, here&#39;s my initial ideas - the first three would be in your welcome pack:&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Update:&lt;/b&gt;Gary Myers came up with a good point, being that any book should really be readable on public transport.  That probably rules out Code Complete (although I read it on the tube, I can see that it&#39;s a little tricky), but Design Patterns and Refactoring to Patterns are small enough I reckon.&lt;br /&gt;&lt;br /&gt;Unfortunately, Code Complete is a &lt;i&gt;really&lt;/i&gt; good book that gives a lot of great, simple, valuable advice.  Does anyone out there have any other suggestions for similar books?&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Update 2:&lt;/b&gt;Andy Beacock reminded me of Fowler&#39;s Refactoring, which really should also make the list.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Update 3:&lt;/b&gt;The development team have bought into the idea and the boss has been asked.  In fact, I&#39;m pretty pleased with the enthusiasm shown by the team for the idea.  I can&#39;t see the boss turning it down.  Interestingly though, someone suggested that Code Complete go onto the list...&lt;br /&gt;&lt;br /&gt;In this order:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;http://astore.amazon.co.uk/bobablog-21/detail/0321278658/203-1487708-4221519&quot; target=&quot;_BLANK&quot;&gt;Extreme Programming Explained - Kent Beck&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href=&quot;http://astore.amazon.co.uk/bobablog-21/detail/020161622X/203-1487708-4221519&quot; target=&quot;_BLANK&quot;&gt;The Pragmatic Programmer - Andy Hunt &amp; Dave Thomas&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href=&quot;http://astore.amazon.co.uk/bobablog-21/detail/0974514012/203-1487708-4221519&quot; target=&quot;_BLANK&quot;&gt;Pragmatic Unit Testing using JUnit - Andy Hunt &amp; Dave Thomas&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;&lt;a href=&quot;http://astore.amazon.co.uk/bobablog-21/detail/0321205685/203-1487708-4221519&quot; target=&quot;_BLANK&quot;&gt;User Stories Applied - Mike Cohn&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href=&quot;http://astore.amazon.co.uk/bobablog-21/detail/0201741172/203-1487708-4221519&quot; target=&quot;_BLANK&quot;&gt;Software Configuration Management Patterns - Steve Berczuk&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href=&quot;http://www.amazon.co.uk/Refactoring-Improving-Design-Existing-Technology/dp/0201485672/ref=pd_bbs_sr_1?ie=UTF8&amp;s=books&amp;qid=1206634040&amp;sr=8-1&quot; target=&quot;_BLANK&quot;&gt;Refactoring: Improving the Design of Existing Code - Martin Fowler&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href=&quot;http://www.amazon.co.uk/Refactoring-Patterns-Addison-Wesley-Signature-Kerievsky/dp/0321213351/ref=sr_1_1?ie=UTF8&amp;s=books&amp;qid=1206468874&amp;sr=1-1&quot; target=&quot;_BLANK&quot;&gt;Refactoring to Patterns - Joshua Kerievsky&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href=&quot;http://www.amazon.co.uk/Design-patterns-elements-reusable-object-oriented/dp/0201633612/ref=sr_1_1?ie=UTF8&amp;s=books&amp;qid=1206468905&amp;sr=1-1&quot; target=&quot;_BLANK&quot;&gt;Design patterns : Elements of Reusable Object-Oriented Software - Gamma, Helm, Johnson, and Vlissides&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href=&quot;http://www.amazon.co.uk/Mythical-Month-Essays-Software-Engineering/dp/0201835959/ref=sr_1_1?ie=UTF8&amp;s=books&amp;qid=1206468972&amp;sr=1-1&quot; target=&quot;_BLANK&quot;&gt;The Mythical Man Month and Other Essays on Software Engineering - Frederick P. Brooks&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;Ruled out because of their size:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;http://www.amazon.co.uk/Code-Complete-Practical-Handbook-Construction/dp/0735619670/ref=sr_1_1?ie=UTF8&amp;s=books&amp;qid=1206468835&amp;sr=8-1&quot; target=&quot;_BLANK&quot;&gt;Code Complete - Steve McConnell&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href=&quot;http://www.amazon.co.uk/Head-First-Design-Patterns/dp/0596007124/ref=sr_1_1?ie=UTF8&amp;s=books&amp;qid=1206468940&amp;sr=1-1&quot; target=&quot;_BLANK&quot;&gt;Head First Design Patterns - Freemen(s), Bates and Sierra&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/3527314385605883422/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/10846234/3527314385605883422' title='9 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/3527314385605883422'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/3527314385605883422'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2008/03/reading-list-for-our-developers.html' title='A reading list for our developers'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJKWDfU-frt3gi_ubtgJ8msvcKcp6ChPvxN_uLMCR-FqygXX-IabdD7vmbQ3RHXnwvmQRPaiXacC6CoIfaYcyh0hGdV0PQzbjFaqf_ECJjuD2JzQVdrINY6YBtjgISt_4/s113/Rob+Baillie.png'/></author><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-3926374224924118442</id><published>2007-09-04T15:54:00.000+01:00</published><updated>2013-08-08T16:27:53.896+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="development"/><category scheme="http://www.blogger.com/atom/ns#" term="Oracle"/><category scheme="http://www.blogger.com/atom/ns#" term="Principles"/><category scheme="http://www.blogger.com/atom/ns#" term="processes"/><title type='text'>Database Build Script &amp;quot;Greatest Hits&amp;quot;</title><content type='html'>I know its been a quiet time on this blog for a while now, but I&#39;ve noticed that I&#39;m still getting visitors looking up old blog posts.  It&#39;s especially true of the posts that relate to &quot;The Patch Runner&quot;.  Many of them come through a link from &lt;a href=&quot;http://www.oratransplant.nl/&quot; target=&quot;_BLANK&quot;&gt;Wilfred van der Deijl&lt;/a&gt;, mainly his great post of &quot;&lt;a href=&quot;http://www.oratransplant.nl/2005/08/03/version-control-of-database-objects&quot; target=&quot;_BLANK&quot;&gt;Version control of Database Objects&lt;/a&gt;&quot;.

The patch runner is my grand idea for a version controlled database build script that you can use to give your developers sandbox databases to play with as well as ensuring that your live database upgrades work first time, every time.

It&#39;s all still working perfectly here, and people still seem to be interested, so with that in mind I&#39;ve decided to collate them a little bit.  basically provide an index of all the posts I&#39;ve made over the years that directly relate to database build scripts, sandboxes and version control.

So, Rob&#39;s database build script &#39;Greatest Hits&#39;:

&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;http://robertbaillie.blogspot.com/2005/06/back-to-oracle_02.html&quot; target=&quot;_BLANK&quot;&gt;On the basic idea of building a database in an agile manner&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://robertbaillie.blogspot.com/2005/08/database-patch-runner.html&quot;&gt;On how you might start putting together a version controlled database build script&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://robertbaillie.blogspot.com/2005/08/database-patch-runner-rollbacks.html&quot; target=&quot;_BLANK&quot;&gt;On how you can organise patches to reduce the pain of a rolling back changes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://robertbaillie.blogspot.com/2005/08/database-patch-runner-table-centric.html&quot; target=&quot;_BLANK&quot;&gt;On the arguments between table-centric and patch-centric database build scripts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://robertbaillie.blogspot.com/2005/08/database-upgrade-scripts-why-all.html&quot; target=&quot;_BLANK&quot;&gt;On what automated build scripts give you&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://robertbaillie.blogspot.com/2005/09/database-patch-runner-design-by.html&quot; target=&quot;_BLANK&quot;&gt;On having the build script be able to tell when something goes wrong, and stop&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://robertbaillie.blogspot.com/2006/01/developing-environment.html&quot; target=&quot;_BLANK&quot;&gt;On how you can use your automated builds to give you a sandbox&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://robertbaillie.blogspot.com/2006/01/we-need-space-to-develop.html&quot; target=&quot;_BLANK&quot;&gt;On why your DBAs shouldn&#39;t burst into tears when you tell them you need a database for each developer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://robertbaillie.blogspot.com/2006/07/version-control-and-patch-runner.html&quot; target=&quot;_BLANK&quot;&gt;On how you database build script can work with you version control tool&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;As well as: &lt;a href=&quot;http://robertbaillie.blogspot.com/2006/03/uk-oug-and-running.html&quot; target=&quot;_BLANK&quot;&gt;a presentation given to the UK OUG&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

All of the posts describe processes and patch runners that are very similar to those that I use in my work every day.  I started playing with these theories over 3 years ago now and there is no way I&#39;d go back to implement database upgrades the way I did before.

However, I&#39;d LOVE to hear ideas on how things can be improved.  I&#39;d be amazed if my three year old thinking was still up to date!

&lt;span class=&quot;technoratitag&quot;&gt;Technorati Tags: &lt;a href=&quot;http://www.technorati.com/tags/oracle,&quot; rel=&quot;tag&quot;&gt;oracle,&lt;/a&gt;, &lt;a href=&quot;http://www.technorati.com/tags/rdbms,&quot; rel=&quot;tag&quot;&gt;rdbms,&lt;/a&gt;, &lt;a href=&quot;http://www.technorati.com/tags/software,&quot; rel=&quot;tag&quot;&gt;software,&lt;/a&gt;, &lt;a href=&quot;http://www.technorati.com/tags/development,&quot; rel=&quot;tag&quot;&gt;development,&lt;/a&gt;, &lt;a href=&quot;http://www.technorati.com/tags/upgrades,&quot; rel=&quot;tag&quot;&gt;upgrades,&lt;/a&gt;, &lt;a href=&quot;http://www.technorati.com/tags/Robert+Baillie&quot; rel=&quot;tag&quot;&gt;Robert+Baillie&lt;/a&gt;&lt;/span&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/3926374224924118442/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/10846234/3926374224924118442' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/3926374224924118442'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/3926374224924118442'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2007/09/database-build-script-greatest-hits.html' title='Database Build Script &amp;quot;Greatest Hits&amp;quot;'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJKWDfU-frt3gi_ubtgJ8msvcKcp6ChPvxN_uLMCR-FqygXX-IabdD7vmbQ3RHXnwvmQRPaiXacC6CoIfaYcyh0hGdV0PQzbjFaqf_ECJjuD2JzQVdrINY6YBtjgISt_4/s113/Rob+Baillie.png'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-211813710969096364</id><published>2007-08-20T16:52:00.000+01:00</published><updated>2007-08-20T16:54:44.354+01:00</updated><title type='text'>Wow</title><content type='html'>And you think software patents are bad...&lt;br /&gt;&lt;br /&gt;&lt;a href=&quot;http://www.msnbc.msn.com/id/20227400/site/newsweek/&quot; target=&quot;_BLANK&quot;&gt;China Regulates Buddhist Reincarnation&lt;/a&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/211813710969096364/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/10846234/211813710969096364' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/211813710969096364'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/211813710969096364'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2007/08/wow.html' title='Wow'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJKWDfU-frt3gi_ubtgJ8msvcKcp6ChPvxN_uLMCR-FqygXX-IabdD7vmbQ3RHXnwvmQRPaiXacC6CoIfaYcyh0hGdV0PQzbjFaqf_ECJjuD2JzQVdrINY6YBtjgISt_4/s113/Rob+Baillie.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-841467377530994339</id><published>2007-07-20T16:58:00.000+01:00</published><updated>2007-07-20T17:30:41.756+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="cvs"/><title type='text'>Problems with CVS removes?</title><content type='html'>&lt;b&gt;Accidently removed a file in CVS that you want to keep?&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Sounds like a stupid question, because when you know the answer to this problem it just seems blindingly obvious, &lt;br /&gt;but what if you&#39;ve issued a &#39;remove&#39; against a file in CVS and before you commit the remove you decided that you &lt;br /&gt;made a mistake and still want to keep it?&lt;br /&gt;&lt;br /&gt;I.E you issued (for example)&lt;br /&gt;&lt;pre&gt;&lt;br /&gt; &gt; cvs remove -f sheep.php&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;But not issued&lt;br /&gt;&lt;pre&gt;&lt;br /&gt; &gt; cvs commit -m removed sheep.php&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I&#39;ve heard work arounds such as:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Edit the &quot;entries&quot; file in the relevant CVS directory in your workspace, removing the reference to the file.&lt;br /&gt;This makes the file appear unknown to CVS.&lt;li&gt;Perform an update in that directory.  This gets the repository version of the file and updates the &quot;entries&quot;&lt;br /&gt;file correctly&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;All you actually need to do is re-add the file:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt; &gt; cvs add sheep.php&lt;br /&gt;&lt;br /&gt; U sheep.php&lt;br /&gt; cvs server: sheep.php, version 1.6, resurrected&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;When used in this way, the add command will issue an update against the file and retrieve the repository version of the file.&lt;br /&gt;&lt;br /&gt;A word of warning though, if you had uncommitted changes in that file before you issued a remove, CVS isn&#39;t going to recover that for you...&lt;br /&gt;&lt;br /&gt;&lt;b&gt;How about if you&#39;ve removed a file, but your version of the file is out of date and so you can&#39;t commit it?&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;So you&#39;ve issued the following:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt; &gt; cvs remove -f sheep.txt &lt;br /&gt;&lt;br /&gt; cvs server: scheduling &#39;sheep.txt&#39; for removal&lt;br /&gt; cvs server: use &#39;cvs sheep&#39; to remove this file permanently&lt;br /&gt;&lt;br /&gt; &gt; cvs commit -m removed sheep.txt &lt;br /&gt;&lt;br /&gt; cvs server: Up-to-date check failed for &#39;sheep.txt&#39;&lt;br /&gt; cvs server: correct above errors first!&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;You can&#39;t issue an update because you get the following:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt; &gt; cvs update sheep.txt&lt;br /&gt;&lt;br /&gt;  cvs server: conflict: removed sheep.txt was modified by second party&lt;br /&gt;  C rob_tmp.txt&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Again, add the file.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt; &gt; cvs add sheep.php&lt;br /&gt;&lt;br /&gt; U sheep.php&lt;br /&gt; cvs server: sheep.php, version 1.6, resurrected&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This gets you the most up to date version from the repository, that you can then check for changes (you wouldn&#39;t want to just remove it now that someone&#39;s added new content would you?)&lt;br /&gt;&lt;br /&gt;Once you&#39;ve convinced yourself that it&#39;s still a good idea to delete it, just issue the remove and commit.&lt;br /&gt;&lt;br /&gt;Simple when you know how!</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/841467377530994339/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/10846234/841467377530994339' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/841467377530994339'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/841467377530994339'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2007/07/problems-with-cvs-removes.html' title='Problems with CVS removes?'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJKWDfU-frt3gi_ubtgJ8msvcKcp6ChPvxN_uLMCR-FqygXX-IabdD7vmbQ3RHXnwvmQRPaiXacC6CoIfaYcyh0hGdV0PQzbjFaqf_ECJjuD2JzQVdrINY6YBtjgISt_4/s113/Rob+Baillie.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-2685212307780651303</id><published>2007-07-12T14:15:00.000+01:00</published><updated>2013-08-08T16:27:11.259+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="development"/><category scheme="http://www.blogger.com/atom/ns#" term="Oracle"/><category scheme="http://www.blogger.com/atom/ns#" term="wtf"/><title type='text'>Can a change in execution plan change the results?</title><content type='html'>We&#39;ve been using Oracle Domain indexes for a while now in order to search documents to get back a ranked order of things that meet certain criteria.

The documents are releated to people, and we augment the basic text search with other filters and score metrics based on the &#39;people&#39; side of things to get an overall &#39;suitability&#39; score for the results in a search.

Without giving too much away about the business I work with I can&#39;t really tell you much more about the product than that, but it&#39;s probably enough of a background for this little gem.

We&#39;ve known for a while that the domain index &#39;score&#39; returned from a &#39;contains&#39; clause is based not only on the document to which that score relates, but also on the rest of the set that is searched.  An individual document score does not live in isolation, rather in lives in the context of the whole result set.

No problem.  As I say, we&#39;ve known this for a while and so have our customers.  Quite a while ago they stopped asking what the numbers mean and learned to trust them.

However, today we realised something.  Since the results are affected by the result set that is searched, this means that the results can be affected by the order in which the optimizer decides to execute a query.

I can&#39;t give you a full end to end example, but I can assure you that the following is most definately the case on one of our production domain indexes (names changed, obviously):

We have a two column table &#39;document_index&#39;, which contains &#39;id&#39; and &#39;document_contents&#39;.  Both columns have an index.  The ID being the primary key and the other being a domain index.

The following SQL gives the related execution path:

SELECT id, SCORE( 1 )
FROM   document_index
WHERE  CONTAINS( document_contents, :1, 1 ) &gt; 0
AND    id = :2

SELECT STATEMENT
  TABLE ACCESS BY INDEX ROWID SCOTT.DOCUMENT_INDEX
    DOMAIN INDEX SCOTT.DOCUMENT_INDEX_IDX01

However, the alternative SQL gives this execution path:

SELECT id, SCORE( 1 )
FROM   document_index
WHERE  CONTAINS( document_contents, &#39;Some text&#39;, 1 ) &gt; 0
AND    id = :2

SELECT STATEMENT
  TABLE ACCESS BY INDEX ROWID SCOTT.DOCUMENT_INDEX                       
    INDEX UNIQUE SCAN SCOTT.DOCUMENT_INDEX_PK                         

Normally, this kind of change in execution path wouldn&#39;t be a problem.  But as stated earlier, the result of a score operation against a domain index is not just dependant on the individual records, but the context of the whole result set.  The first execution provides you a score for the single document in the context of the all the documents in the table, the second gives you a score within the context of just that document.  The scores are different.

Now obviously, this is an extreme example, but more subtle examples will almost certainly exist if you combine the domain index lookups with any other where clause criteria.  This is especially true if you&#39;re using literal values instead of bind variables in which case you may find the execution path changing between calls to the &#39;same&#39; piece of SQL.

My advice?  Well, we&#39;re going to split our domain index look ups from all the rest of the filtering criteria, that way we can prepare the set of documents we want the search to be within and know that the scoring algorithm will be applied consistently.</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/2685212307780651303/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/10846234/2685212307780651303' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/2685212307780651303'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/2685212307780651303'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2007/07/can-change-in-execution-plan-change.html' title='Can a change in execution plan change the results?'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJKWDfU-frt3gi_ubtgJ8msvcKcp6ChPvxN_uLMCR-FqygXX-IabdD7vmbQ3RHXnwvmQRPaiXacC6CoIfaYcyh0hGdV0PQzbjFaqf_ECJjuD2JzQVdrINY6YBtjgISt_4/s113/Rob+Baillie.png'/></author><thr:total>0</thr:total></entry></feed>