tag:blogger.com,1999:blog-72245488944655895012024-03-05T08:57:39.812-06:00People are the platform - Brad BroulikBrad Broulikhttp://www.blogger.com/profile/14248585105589786365noreply@blogger.comBlogger55125tag:blogger.com,1999:blog-7224548894465589501.post-55733002144585193462020-05-18T21:14:00.001-05:002020-05-18T21:19:01.560-05:00The Strangest Secret for the Perfect Team<style>
/* ///////////////////////////////////////////////// */
/* custom styles */
/* ///////////////////////////////////////////////// */
html {
font: normal 100%/1.5rem "SF Pro Text", "SF Pro Icons", "Helvetica Neue", "Helvetica", "Arial", sans-serif !important; /* font-size: ~16px, line-height: ~24px; */
}
h1, h4 { margin-bottom:0; }
h1 {
font-size: 1.5rem; /* 24px / 16px */
font-weight: normal;
}
h4 {
font-size: 1.125rem; /* 20px / 16px */
font-weight: bold;
line-height: 1.5rem;
}
h5 {
font-size: .875rem; /* 14px / 16px */
font-weight: bold;
line-height: 1.5rem;
}
p {
font-size: 1rem; /* 16px / 16px */
line-height: 1.5rem!important;
margin-top: .5em;
}
li {
font-size: 1rem; /* 16px / 16px */
line-height: 1.25rem!important;
margin-top: .5em;
}
.hljs { background:#F9FAFA!important; }
a { text-decoration: none; }
br.clear { clear:both; }
span.code-highlight1 {
font-style: italic;
color: green;
}
a.button:link, a.button:visited {
display: block;
width: 16em;
padding: 0.2em;
line-height: 1.4;
background-color: #94B8E9;
border: 1px solid black;
color: #000;
text-decoration: none;
text-align: center;
}
a.button:hover {
background-color: #369;
color: #fff;
}
table {
margin-left:2px;
margin-right:2px;
border-collapse: collapse;
font-family: "Century Gothic", "Gill Sans", Arial, sans-serif;
}
caption {
text-align: center;
}
td,th {
border: 1px solid #73afb7;
padding: 3px 5px 2px 5px;
}
th {
background:#93C2C8 repeat;
color: white;
text-align: left;
border-color: #14556b;
}
tr.alt td {
background:#F6F8EE repeat;
}
/* ///////////////////////////////////////////////// */
/* End custom styles */
/* ///////////////////////////////////////////////// */
</style>
<link href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.9.0/styles/vs.min.css" rel="stylesheet"></link>
<!--JSON-LD markup generated by Google Structured Data Markup Helper.-->
<script type="application/ld+json">
{
"@context" : "http://schema.org",
"@type" : "Article",
"headline" : "Faster Websites in 60 Seconds with preload and prefetch",
"name" : "Faster Websites in 60 Seconds with preload and prefetch",
"author" : {
"@type" : "Person",
"name" : "Brad Broulik"
},
"datePublished" : "2019-12-15",
"image" : "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhuPefbIXBZWs2QIV0qTzRSXNlqEoEHH9fiL14jXK_eRb-Zvpjknovj1rERwhapU2lDlWy3nJSpFeJWeKIk100YHdDDmE2X7SpTntD-a2hV1neJFDe64yB_9SirhdQbdkdwSbncS5zFCUk/s80/BradBroulikBioPhoto-sent.jpg",
"articleSection" : "Faster Websites in 60 Seconds with preload and prefetch",
"articleBody" : "Preloading assets tells the browser to load critical assets earlier in the page loading lifecycle and as a result improves page load performance.",
"url" : "http://bradbroulik.blogspot.com/2019/12/seo-tips-to-boost-revenue.html",
"publisher" : {
"@type" : "Organization",
"logo": {
"@type": "ImageObject",
"url": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhuPefbIXBZWs2QIV0qTzRSXNlqEoEHH9fiL14jXK_eRb-Zvpjknovj1rERwhapU2lDlWy3nJSpFeJWeKIk100YHdDDmE2X7SpTntD-a2hV1neJFDe64yB_9SirhdQbdkdwSbncS5zFCUk/s113/BradBroulikBioPhoto-sent.jpg"
},
"name" : "Brad Broulik"
}
}
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.9.0/highlight.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.9.0/languages/swift.min.js"></script>
<script>hljs.initHighlightingOnLoad();</script>
<figure style="float: right;"><img alt="The Office" border="0" data-original-height="368" data-original-width="271" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiw0AVKNiib0KlZ8nsR9dKCWgwlCmnqCkXIwHdyHQX4H5I_1AIEaxnydLb5AWQYWwq62ILb68W6RU9hpnhED3HYI-P_A0qPpUPUhb9qDtGRaRA4c_Xq6pHW-C_C_vriy_6egcoceQkZR9M_/s200/TheOffice_S7_DVD.jpg" width="146" /><figcaption style="text-align: left;"><em></em></figcaption></figure>
<br />
<p>Get your pen and paper ready. What do you think is the #1 ingredient for making the perfect team? Is it communication, technical expertise or a dash of something else. My slide deck with the answer to this golden question is available at <a href="https://bit.ly/the-perfect-team">bit.ly/the-perfect-team</a>. <br />
<br />
Is <a href="https://en.wikipedia.org/wiki/The_Office_(American_TV_series)">The Office</a> a perfect team? The Office is classic TV series known for its slapstick comedy. However, it provides many anti-patterns of the perfect team. Let's start by discussing a few of those anti-pattern ingredients of the perfect team:</p>
<ul>
<li>Fear - most of the employees in The Office went to work fearful if they made a mistake a co-worker would call them out and they may lose their job.</li>
<li>Bullying - Some employees took advantage of their power or intimidated the vulnerable characters in the office.</li>
<li>Name calling - Most employees were called names for their beliefs, habits or lifestyle. </li>
<li>Inappropriate jokes - These jokes made The Office a classic but are not meant for a "real" office.</li>
</ul>
<h4>
Project Aristotle</h4>
<p><a href="https://rework.withgoogle.com/print/guides/5721312655835136/">Google's Project Aristotle</a> was a research study to determine what makes a perfect team (see Figure 1). Project Aristotle analyzed data on inventive and productive teams. Their study spanned 2 years, 180 teams, 37,000 employees and they invested millions in the quest to find the answer to the golden question. With this much time and money invested my ears were wide open for the answer.</p><br />
<figure style="float: clear;"><img border="0" data-original-height="1114" data-original-width="1454" height="244" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjxc8aGeeso42YpgCZnN2bePkkrDpCVZAm9tdshCAF7tu1_9hYrwe_gkwDh4sWjYkA0Emra3S-g3ofRDg_iNssdvfBQaW2J-QeqDbpHjCFBNT2641wYcnyyZ0Yb7Y1C65uUakbCEOFZmm3T/s320/slide3-perfect-team.png" width="320" /><figcaption style="text-align: left;"><em>Figure 1. Google's Project Aristotle Study</em></figcaption></figure>
<br />
<p>Drum roll please... The answer is, <b><em>psychological safety</em></b>. Project Aristotle shows that the <mark>best teams at Google exhibit a range of soft skills</mark>: equality, generosity, curiosity toward the ideas of your teammates, empathy, and emotional intelligence. However, topping the list was <mark>emotional safety</mark> which has two key ingredients. 1) No bullying. And 2) <mark>to succeed, each and every team member must feel confident speaking up and making mistakes. They must know they are being heard</mark>. However, we desperately need the expertise of those who are educated to the <mark>human, cultural, and social as well as the computational</mark>.</p>
<h4>
Is your team perfect?</h4>
<p>Take your team photo, place it in the picture frame in figure 2 and ask yourself, is this a perfect team? If not, share this study with your team, your department, or your company because the perfect ingredient is a handful of psychological safety.</p>
<figure style="float: clear;"><img border="0" data-original-height="1053" data-original-width="1600" height="210" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgALD-YRpc4_fA-E3Vh4j8_EmK8RBqtrf91yfGALDVCA3MM0-xH0pdLRzgwYkGM18mQlbodJZEoq1Lz8MP67-X331lEp-LUcwlRLJ4Fz-5oFbK54vhReMpVgq7tyt9-mlnZeXFA9Bq_ohR0/s320/perfect-team-is-yours.png" width="320" /><figcaption style="text-align: left;"><em>Figure 2. Is your team perfect?</em></figcaption>
Brad Broulikhttp://www.blogger.com/profile/14248585105589786365noreply@blogger.com0tag:blogger.com,1999:blog-7224548894465589501.post-34031191507554351422019-12-15T12:21:00.000-06:002019-12-15T12:21:07.326-06:00Faster Websites in 60 Seconds with preload and prefetch<style>
/* ///////////////////////////////////////////////// */
/* custom styles */
/* ///////////////////////////////////////////////// */
html {
font: normal 100%/1.5rem "SF Pro Text", "SF Pro Icons", "Helvetica Neue", "Helvetica", "Arial", sans-serif !important; /* font-size: ~16px, line-height: ~24px; */
}
h1, h4 { margin-bottom:0; }
h1 {
font-size: 1.5rem; /* 24px / 16px */
font-weight: normal;
}
h4 {
font-size: 1.125rem; /* 20px / 16px */
font-weight: bold;
line-height: 1.5rem;
}
h5 {
font-size: .875rem; /* 14px / 16px */
font-weight: bold;
line-height: 1.5rem;
}
p {
font-size: 1rem; /* 16px / 16px */
line-height: 1.5rem!important;
margin-top: .5em;
}
li {
font-size: 1rem; /* 16px / 16px */
line-height: 1.25rem!important;
margin-top: .5em;
}
.hljs { background:#F9FAFA!important; }
a { text-decoration: none; }
br.clear { clear:both; }
span.code-highlight1 {
font-style: italic;
color: green;
}
a.button:link, a.button:visited {
display: block;
width: 16em;
padding: 0.2em;
line-height: 1.4;
background-color: #94B8E9;
border: 1px solid black;
color: #000;
text-decoration: none;
text-align: center;
}
a.button:hover {
background-color: #369;
color: #fff;
}
table {
margin-left:2px;
margin-right:2px;
border-collapse: collapse;
font-family: "Century Gothic", "Gill Sans", Arial, sans-serif;
}
caption {
text-align: center;
}
td,th {
border: 1px solid #73afb7;
padding: 3px 5px 2px 5px;
}
th {
background:#93C2C8 repeat;
color: white;
text-align: left;
border-color: #14556b;
}
tr.alt td {
background:#F6F8EE repeat;
}
/* ///////////////////////////////////////////////// */
/* End custom styles */
/* ///////////////////////////////////////////////// */
</style>
<link href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.9.0/styles/vs.min.css" rel="stylesheet"></link>
<!-- JSON-LD markup generated by Google Structured Data Markup Helper. -->
<script type="application/ld+json">
{
"@context" : "http://schema.org",
"@type" : "Article",
"headline" : "Faster Websites in 60 Seconds with preload and prefetch",
"name" : "Faster Websites in 60 Seconds with preload and prefetch",
"author" : {
"@type" : "Person",
"name" : "Brad Broulik"
},
"datePublished" : "2019-12-15",
"image" : "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhuPefbIXBZWs2QIV0qTzRSXNlqEoEHH9fiL14jXK_eRb-Zvpjknovj1rERwhapU2lDlWy3nJSpFeJWeKIk100YHdDDmE2X7SpTntD-a2hV1neJFDe64yB_9SirhdQbdkdwSbncS5zFCUk/s80/BradBroulikBioPhoto-sent.jpg",
"articleSection" : "Faster Websites in 60 Seconds with preload and prefetch",
"articleBody" : "Preloading assets tells the browser to load critical assets earlier in the page loading lifecycle and as a result improves page load performance.",
"url" : "http://bradbroulik.blogspot.com/2019/12/seo-tips-to-boost-revenue.html",
"publisher" : {
"@type" : "Organization",
"logo": {
"@type": "ImageObject",
"url": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhuPefbIXBZWs2QIV0qTzRSXNlqEoEHH9fiL14jXK_eRb-Zvpjknovj1rERwhapU2lDlWy3nJSpFeJWeKIk100YHdDDmE2X7SpTntD-a2hV1neJFDe64yB_9SirhdQbdkdwSbncS5zFCUk/s113/BradBroulikBioPhoto-sent.jpg"
},
"name" : "Brad Broulik"
}
}
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.9.0/highlight.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.9.0/languages/swift.min.js"></script>
<script>hljs.initHighlightingOnLoad();</script>
<figure style="float: right;"><img border="0" data-original-height="310" data-original-width="338" height="183" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgyODDqHogXuCIS5irlqBWtgR3JEA_mb-m8gqn-NY604WsUpUcbuXZMYtzN7ObfrAPMVojoxJlunU3QFIAm3vYg0yx2W6lG8CreETCt30_8K_1d785TZV8vA2cK8mgNad3UjHQHKgYqYXVb/s200/performance.png" width="200" /><figcaption style="text-align: left;"><em></em></figcaption></figure>
<p>Are efficient developers lazy or are lazy developers efficient? Ask this open ended question at your next interview and you will have an interesting conversation. While the goal is to improve performance, if we can do it in a lazy fashion (60 seconds or less) it's a win-win! This performance tip is rarely applied in our industry and we're all missing out! According to the <a href="https://almanac.httparchive.org/en/2019/">2019 State of the Web Report</a>, only <em>14%</em> of desktop site are using <em><a href="https://almanac.httparchive.org/en/2019/javascript#preload-and-prefetch">preload</a></em> and about <em>1%</em> are using <em><a href="https://almanac.httparchive.org/en/2019/javascript#preload-and-prefetch">prefetch</a></em>.</p>
<h4>
preload</h4>
<p><a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Preloading_content">Preloading</a> assets tells the browser to load critical assets earlier in the page loading lifecycle and as a result improves page load performance. Critical assets are the resources (JavaScript, CSS, images, fonts, etc) that are required to render the initial page or the displayable screen above the fold. For examples of <em>preloading</em> assets refer to <em>Listing 1</em>. It's very important to include the <em>as</em> attribute when preloading assets because it sets the content-type header, accept header, loading priority, and helps cache the asset for future requests.
A complete listing of the content types that can be preloaded are listed <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Preloading_content#What_types_of_content_can_be_preloaded">here</a>.</p><br />
<figure style="float: clear;"><pre style="background-color: #f9fafa; border: 1px solid #d6d6d6; color: black; font-family: "arial"; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; white-space: pre-wrap; width: 99%;"><code class="swift" style="background: #f9fafa; color: black;">// Without preloading
<link rel="stylesheet" href="fancybox.css">
<script src="fancybox.js"></script>
// With preloading
<link <b>rel="preload"</b> href="preloaded-image.jpg" <b>as="image"</b>>
<link <b>rel="preload"</b> href="fancybox.css" <b>as="style"</b>>
<link <b>rel="preload"</b> href="fancybox.js" <b>as="script"</b>>
</code></pre>
<em>Listing 1. Preload critical assets</em></figure>
<br />
<h4>
prefetch</h4>
<p><a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Link_prefetching_FAQ">Prefetching</a> assets tells the browser to load these non-critical assets in the background after the page loading lifecycle has completed. As a result, these assets will be cached for future page navigations which greatly improves page load performance. For examples of <em>prefetching</em> assets refer to <em>Listing 2</em>.</p>
<br />
<figure style="float: clear;"><pre style="background-color: #f9fafa; border: 1px solid #d6d6d6; color: black; font-family: "arial"; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; white-space: pre-wrap; width: 99%;"><code class="swift" style="background: #f9fafa; color: black;">// Without prefetching
<link rel="stylesheet" href="fancybox.css">
<script src="fancybox.js"></script>
// With prefetching
<link <b>rel="prefetch"</b> href="preloaded-image.jpg" <b>as="image"</b>>
<link <b>rel="prefetch"</b> href="fancybox.css" <b>as="style"</b>>
<link <b>rel="prefetch"</b> href="fancybox.js" <b>as="script"</b>>
</code></pre>
<em>Listing 2. Prefetch non-critical assets</em></figure><br />
<h4>
Auditing Performance</h4>
<p>How much can your site gain by preloading and prefetching assets? Find out using <a href="https://developers.google.com/web/tools/lighthouse">Lighthouse in Chrome's Dev Tools</a> (see Figure 2). Check the "Performance" option and run the audit. Then refer to your estimated savings report to see your performance improvement opportunity (see Figure 3). A faster site will also boost your <a href="http://bradbroulik.blogspot.com/2019/12/seo-tips-to-boost-revenue.html">SEO score</a> so this lazy technique has an additional benefit of boosting revenue!</p>
<br />
<figure style="float: clear;"><img border="0" data-original-height="806" data-original-width="872" height="295" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiu0pnim4SbqC8LihCFrpbPNRwaoYtWOhTAckWZZOKn2aDtSDhHZubGJE7tK1V_TKU6yOCvwsOq6qBBLMsY4LVS_AElNDi56zMrrE6pRlofubH7H2mpoUompMmpsAdXu22WaTUW_OMgiz6V/s320/resource-hints-audit.png" width="320" /><figcaption style="text-align: left;"><em>Figure 2. Chrome's Performance Audit via Lighthouse</em></figcaption></figure>
<br />
<figure style="float: clear;"><img border="0" data-original-height="224" data-original-width="1472" height="96" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjun_X7GRCDRF0jhRBdEkceV9ySndW-1oEIeSb_JEPipnZVxtpa_1lQGfaIZGL-9UPD2F2hQf5kYAQaRMjG2T0vdWJphKuQ90-JifJ9LS_rAuq_PI2-WLvRz__HoQjrRC3ehndEc5RXB_c1/s640/resource-hints-savings.png" width="640" /><figcaption style="text-align: left;"><em>Figure 3. Performance savings report by preloading and prefetching</em></figcaption></figure>Brad Broulikhttp://www.blogger.com/profile/14248585105589786365noreply@blogger.com0tag:blogger.com,1999:blog-7224548894465589501.post-60307020768313744812019-12-04T20:51:00.000-06:002019-12-15T12:22:42.698-06:00SEO Tips to Boost Revenue<style>
/* ///////////////////////////////////////////////// */
/* custom styles */
/* ///////////////////////////////////////////////// */
html {
font: normal 100%/1.5rem "SF Pro Text", "SF Pro Icons", "Helvetica Neue", "Helvetica", "Arial", sans-serif !important; /* font-size: ~16px, line-height: ~24px; */
}
h1, h4 { margin-bottom:0; }
h1 {
font-size: 1.5rem; /* 24px / 16px */
font-weight: normal;
}
h4 {
font-size: 1.125rem; /* 20px / 16px */
font-weight: bold;
line-height: 1.5rem;
}
h5 {
font-size: .875rem; /* 14px / 16px */
font-weight: bold;
line-height: 1.5rem;
}
p {
font-size: 1rem; /* 16px / 16px */
line-height: 1.5rem!important;
margin-top: .5em;
}
li {
font-size: 1rem; /* 16px / 16px */
line-height: 1.25rem!important;
margin-top: .5em;
}
.hljs { background:#F9FAFA!important; }
a { text-decoration: none; }
br.clear { clear:both; }
span.code-highlight1 {
font-style: italic;
color: green;
}
a.button:link, a.button:visited {
display: block;
width: 16em;
padding: 0.2em;
line-height: 1.4;
background-color: #94B8E9;
border: 1px solid black;
color: #000;
text-decoration: none;
text-align: center;
}
a.button:hover {
background-color: #369;
color: #fff;
}
table {
margin-left:2px;
margin-right:2px;
border-collapse: collapse;
font-family: "Century Gothic", "Gill Sans", Arial, sans-serif;
}
caption {
text-align: center;
}
td,th {
border: 1px solid #73afb7;
padding: 3px 5px 2px 5px;
}
th {
background:#93C2C8 repeat;
color: white;
text-align: left;
border-color: #14556b;
}
tr.alt td {
background:#F6F8EE repeat;
}
/* ///////////////////////////////////////////////// */
/* End custom styles */
/* ///////////////////////////////////////////////// */
</style>
<link href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.9.0/styles/vs.min.css" rel="stylesheet"></link>
<!-- JSON-LD markup generated by Google Structured Data Markup Helper. -->
<script type="application/ld+json">
{
"@context" : "http://schema.org",
"@type" : "Article",
"headline" : "10 Tips for Growing Healthy Team Relationships",
"name" : "10 Tips for Growing Healthy Team Relationships",
"author" : {
"@type" : "Person",
"name" : "Brad Broulik"
},
"datePublished" : "2019-12-04",
"image" : "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhuPefbIXBZWs2QIV0qTzRSXNlqEoEHH9fiL14jXK_eRb-Zvpjknovj1rERwhapU2lDlWy3nJSpFeJWeKIk100YHdDDmE2X7SpTntD-a2hV1neJFDe64yB_9SirhdQbdkdwSbncS5zFCUk/s80/BradBroulikBioPhoto-sent.jpg",
"articleSection" : "10 Tips for Growing Healthy Team Relationships",
"articleBody" : "How much can your company lose if your Search Engine Optimization (SEO) strategy is poorly designed? 87% in profit loss! That's right, Asos learned this lesson the hard way.",
"url" : "http://bradbroulik.blogspot.com/2019/12/faster-websites-in-60-seconds-with.html",
"publisher" : {
"@type" : "Organization",
"logo": {
"@type": "ImageObject",
"url": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhuPefbIXBZWs2QIV0qTzRSXNlqEoEHH9fiL14jXK_eRb-Zvpjknovj1rERwhapU2lDlWy3nJSpFeJWeKIk100YHdDDmE2X7SpTntD-a2hV1neJFDe64yB_9SirhdQbdkdwSbncS5zFCUk/s113/BradBroulikBioPhoto-sent.jpg"
},
"name" : "Brad Broulik"
}
}
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.9.0/highlight.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.9.0/languages/swift.min.js"></script>
<script>hljs.initHighlightingOnLoad();</script>
<figure style="float: right;"><img alt="Profit loss" border="0" height="129" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjjh53HNliuUG0vdZEuXq0lMfOnJoq33G0L2bf80xtHF3TieKJbZJYfmIrqNmBBo2ZvHWbWWFG5Uq2_IvzuAnoVYwYnEuTM6PklfoHnENerZVwb99e0OLMa9Ela9rvAViiZQmSpf9Z394vy/s1600/seo-profit.png" width="189" /><figcaption style="text-align: left;"><em></em></figcaption></figure>
<p>How much can your company lose if your Search Engine Optimization (SEO) strategy is poorly designed? <a href="https://www.bbc.com/news/business-47877688">87% in profit loss!</a> That's right, Asos learned this lesson the hard way. Their root cause was attributed to a poor site redesign. Asos released 200 locale-specific versions of their site which negatively affected their search engine rankings and ultimately their profitability. How can we build an effective SEO strategy? Let's review a few basic and advanced techniques to keep our SEO and profits in the right direction.</p>
<h4>
SEO Fundamentals</h4>
<ul>
<li><a href="https://moz.com/learn/seo/title-tag">Title tag</a> - The most important SEO element is the <em><title>My page title</title></em> tag. The title's description becomes the pages title as shown in the search engine response page (SERP). The title tag may be up to 50 characters in length.</li>
<li><a href="https://moz.com/learn/seo/meta-description">Meta description</a> - The 2nd most important SEO element is the <em><meta name="description" content="My description text up to 155 characters..." ></em> The meta content becomes your pages description as shown in the search engine response page (SERP).</li>
</ul>
<h4>
Advanced SEO</h4>
<p>We can now add <a href="https://en.wikipedia.org/wiki/JSON-LD">structured data (JSON-LD)</a> to our content which has multiple benefits: 1) It is an opportunity to show additional search engine response data. 2) It provides rich and meaningful information about our sites. For example, the new <a href="https://developers.google.com/search/docs/data-types/software-app">Software app</a> component allows us to list our native mobile apps within our search results (see Figure 1).</p>
<figure style="float: clear;"><img alt="software app structured data" border="0" data-original-height="580" data-original-width="1172" height="158" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjruC96YceZwMRA3GljYyBmMSkD6sQHlCmEM7nMoXnwZg1H4ac7I0t6vahi1n3XWhcYm_9LAIXbMM3PHL5OMhQVLJbPlQKsvvKmWUr95xhiCBWNMxI8vt7tFZ_kJSx9R03qnPdEmDTqhHos/s320/structured-data-app.png" width="320" /><figcaption style="text-align: left;"><em>Figure 1. Structured data for Software app listing (New)</em></figcaption></figure>
<br />
<p>The following are just a few of the structured data elements available today:</p>
<ul>
<li><a href="https://developers.google.com/search/docs/data-types/article">Articles</a></li>
<li><a href="https://developers.google.com/search/docs/data-types/faqpage">FAQ</a></li>
<li><a href="https://developers.google.com/search/docs/data-types/local-business">Local business listing</a></li>
<li><a href="https://developers.google.com/search/docs/data-types/product">Products</a></li>
<li><a href="https://developers.google.com/search/docs/data-types/software-app">Software app</a></li>
<li><a href="https://developers.google.com/search/docs/data-types/review-snippet">Reviews</a></li>
<li>and <a href="https://developers.google.com/search/docs/guides/search-gallery">more structured data components</a></li>
</ul>
<p>Need help authoring and validating your structured data? Google has a <a href="https://www.google.com/webmasters/markup-helper/">Structured Data Markup Helper</a> and <a href="https://search.google.com/structured-data/testing-tool">Structured Data Testing Tool</a> to help assert your data is correct.</p>
<h4>
Auditing SEO</h4>
<p>Will your site pass an SEO audit? Let's find out using <a href="https://developers.google.com/web/tools/lighthouse">Lighthouse in Chrome's Dev Tools</a> (see Figure 2). Check the "SEO" audit option, run the audit, fix any errors, and your profits will soar!</p>
<figure style="float: clear;"><img border="0" data-original-height="960" data-original-width="738" height="520" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiqf9dYo2H0Bz8eOg6d8aT5LK-t4djSlmm_XjJxdOpGGfGyeohiI-DgriD5t_ulgZOc7F9R2W-GOkGpfXU1ZBP4_cdpdrpQymAytChmarWtogfTc9s5uCOly3HS2tZxt5ui3cALNPqnwaHM/s440/seo-audit.png" alt="Chrome SEO audit" width="400" /><figcaption style="text-align: left;"><em>Figure 2. Chrome's SEO Audit via Lighthouse</em></figcaption></figure>
Brad Broulikhttp://www.blogger.com/profile/14248585105589786365noreply@blogger.com0tag:blogger.com,1999:blog-7224548894465589501.post-31848268321597618182019-06-23T14:39:00.000-05:002019-12-04T20:15:19.736-06:0010 Tips for Growing Healthy Team Relationships<style>
/* ///////////////////////////////////////////////// */
/* custom styles */
/* ///////////////////////////////////////////////// */
html {
font: normal 100%/1.5rem "SF Pro Text", "SF Pro Icons", "Helvetica Neue", "Helvetica", "Arial", sans-serif !important; /* font-size: ~16px, line-height: ~24px; */
}
h1, h4 { margin-bottom:0; }
h1 {
font-size: 1.5rem; /* 24px / 16px */
font-weight: normal;
}
h4 {
font-size: 1.125rem; /* 20px / 16px */
font-weight: bold;
line-height: 1.5rem;
}
h5 {
font-size: .875rem; /* 14px / 16px */
font-weight: bold;
line-height: 1.5rem;
}
p {
font-size: 1rem; /* 16px / 16px */
line-height: 1.5rem!important;
margin-top: .5em;
}
li {
font-size: 1rem; /* 16px / 16px */
line-height: 1.25rem!important;
margin-top: .5em;
}
.hljs { background:#F9FAFA!important; }
a { text-decoration: none; }
br.clear { clear:both; }
span.code-highlight1 {
font-style: italic;
color: green;
}
a.button:link, a.button:visited {
display: block;
width: 16em;
padding: 0.2em;
line-height: 1.4;
background-color: #94B8E9;
border: 1px solid black;
color: #000;
text-decoration: none;
text-align: center;
}
a.button:hover {
background-color: #369;
color: #fff;
}
table {
margin-left:2px;
margin-right:2px;
border-collapse: collapse;
font-family: "Century Gothic", "Gill Sans", Arial, sans-serif;
}
caption {
text-align: center;
}
td,th {
border: 1px solid #73afb7;
padding: 3px 5px 2px 5px;
}
th {
background:#93C2C8 repeat;
color: white;
text-align: left;
border-color: #14556b;
}
tr.alt td {
background:#F6F8EE repeat;
}
/* ///////////////////////////////////////////////// */
/* End custom styles */
/* ///////////////////////////////////////////////// */
</style>
<link href='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.9.0/styles/vs.min.css' rel='stylesheet'/>
<!-- JSON-LD markup generated by Google Structured Data Markup Helper. -->
<script type="application/ld+json">
{
"@context" : "http://schema.org",
"@type" : "Article",
"headline" : "10 Tips for Growing Healthy Team Relationships",
"name" : "10 Tips for Growing Healthy Team Relationships",
"author" : {
"@type" : "Person",
"name" : "Brad Broulik"
},
"datePublished" : "2019-06-23",
"image" : "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhuPefbIXBZWs2QIV0qTzRSXNlqEoEHH9fiL14jXK_eRb-Zvpjknovj1rERwhapU2lDlWy3nJSpFeJWeKIk100YHdDDmE2X7SpTntD-a2hV1neJFDe64yB_9SirhdQbdkdwSbncS5zFCUk/s80/BradBroulikBioPhoto-sent.jpg",
"articleSection" : "Principles for Improving Team Health",
"articleBody" : "How to Win Friends and Influence People</A> by Dale Carnegie is one of the <A href=\"https://en.wikipedia.org/wiki/How_to_Win_Friends_and_Influence_People\">best-selling books of all time</A>. I read this book in 2014 and since then I find myself referring to the notes from this book more than any book I own. The principles discussed within this book are timeless and apply equally well for teams as they do individuals. Why is this book so important for team health and team success? Dale Carnegie answers this question in his introduction when he explains why he wrote this book. He explains the #1 trait for success is human engineering or how well we work with people",
"url" : "http://bradbroulik.blogspot.com/2019/06/10-tips-for-growing-healthy-team.html",
"publisher" : {
"@type" : "Organization",
"logo": {
"@type": "ImageObject",
"url": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhuPefbIXBZWs2QIV0qTzRSXNlqEoEHH9fiL14jXK_eRb-Zvpjknovj1rERwhapU2lDlWy3nJSpFeJWeKIk100YHdDDmE2X7SpTntD-a2hV1neJFDe64yB_9SirhdQbdkdwSbncS5zFCUk/s113/BradBroulikBioPhoto-sent.jpg"
},
"name" : "Brad Broulik"
}
}
</script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.9.0/highlight.min.js'></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.9.0/languages/swift.min.js'></script>
<script>hljs.initHighlightingOnLoad();</script>
<figure style="float: right;"><img alt="How to win friends and influence people" border="1" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjVm5p7GJlxatOEXO3RNSRVx6fphy2z_jahWMvtuaD-eZ_mxB2xuaoDTd6P_pQdqWD6anVkGje3yZe8lL0fMst6T_IAs1crPQDryQ2JUAiWf5ckQF-n_Ag386h0Bjs0J6XXCYJ7PqVo8bDo/s320/book1.jpg" width="161" height="250" /><figcaption style="text-align: left;"><em></em></figcaption></figure>
<p><a href="https://www.amazon.com/How-Win-Friends-Influence-People/dp/0671027034">How to Win Friends and Influence People</a> by Dale Carnegie is one of the <a href="https://en.wikipedia.org/wiki/How_to_Win_Friends_and_Influence_People">best-selling books of all time</a>. I read this book in 2014 and since then I find myself referring to the notes from this book more than any book I own. The principles discussed within this book are timeless and apply equally well for teams as they do individuals. Why is this book so important for team health and team success? Dale Carnegie answers this question in his introduction when he explains why he wrote this book. He explains the #1 trait for success is human engineering or how well we work with people (see Figure 1).</p>
<figure style="float:clear;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjBZfLGUcsjgp6R8FOv4WMbXzCDSRDjr1Boyjn7wHtKTp84JI008togLb7vm7eNkH-uyT011WXhlzcDq7PgiqX6oWwhMzP-7M9NHbE0N1FUkDpvYyuLxliSlBJB-2_utOgOnIFE2pHhBDum/s320/team-success-factors.png" width="304" height="320" data-original-width="574" data-original-height="604" alt="Success factors" /><figcaption style="text-align: left;"><em>Figure 1. Most valuable traits for success</em></figcaption></figure>
<h4>Principles for Improving Team Health</h4>
<p>The listing below contains all the principles Dale Carnegie discusses in his book. While each principle is very important I'm highlighting a few key points that also help grow healthier team relationships.</p>
<p>
<h5>Part I - Fundamental techniques in handling people</h5>
<ul>
<li>Principle #1: <mark><b>Don’t criticize, condemn or complain.</b></mark></li>
<li>Principle #2: <mark><b>Give honest, sincere appreciation.</b></mark></li>
<li>Principle #3: <mark><b>Arouse in the other person an eager want.</b><mark></li>
</ul>
<h5>Part II - Six ways to make people like you</h5>
<ul>
<li>Principle #1: Become genuinely interested in other people.</li>
<li>Principle #2: <mark><b>Smile</b></mark></li>
<li>Principle #3: <mark><b>Remember that a persons name to them is the sweetest and most important sound in any language.</b></mark></li>
<li>Principle #4: Be a good listener and encourage people to talk about themselves.
<li>Principle #5: Talk in terms of the other persons interests.</li>
<li>Principle #6: Make the other person feel important and do it sincerely.</li>
</ul>
<h5>Part III - How to win people to your way of thinking</h5>
<ul>
<li>Principle #1: <mark><b>The only way to get the best of an argument is to avoid it.</b></mark></li>
<li>Principle #2: <mark><b>Show respect for the other persons opinions. Never say you are wrong.</b></mark></li>
<li>Principle #3: <mark><b>If you’re wrong admit it quickly and emphatically.</b></mark></li>
<li>Principle #4: Begin in a friendly way.</li>
<li>Principle #5: Get the other person saying “yes, yes” immediately.</li>
<li>Principle #6: Let the other person do a great deal of the talking.</li>
<li>Principle #7: Let the other person feel that the idea is theirs.</li>
<li>Principle #8: <mark><b>Try honestly to see things from the other persons point of view.</b></mark> <em>(Dale Carnegie said this was the most important thing to remember from the book!)</em></li>
<li>Principle #9: By sympathetic with other persons ideas and desires.</li>
<li>Principle #10: Appeal to the nobler motives.</li>
<li>Principle #11: Dramatize your ideas.</li>
<li>Principle #12: Throw down a challenge.</li>
</ul>
<h5>Part IV - Be a leader: How to change people without giving offense or arousing resentment</h5>
<ul>
<li>Principle #1: Beginning with praise and honest appreciation if you must find fault.</li>
<li>Principle #2: Call attention to peoples mistakes indirectly.</li>
<li>Principle #3: Talk about your own mistakes before criticizing the other person.</li>
<li>Principle #4: <mark><b>Ask questions instead of giving direct orders.</b></mark></li>
<li>Principle #5: Let the other person save face.</li>
<li>Principle #6: Praise the slightest improvement and praise every improvement be hearty in your praise.</li>
<li>Principle #7: <mark><b>Give the other person a fine reputation to live up to.</b></mark></li>
<li>Principle #8: Use encouragement and make the fault seem easy to correct.</li>
<li>Principle #9: Make the other person happy about doing the thing you suggest.</li>
</ul>
</p>
<br/>
<br/>
<hr>
<br/>
<h4>Favorite quotes for each highlighted principle</h4>
<h5>Don’t criticize, condemn or complain</h5>
<ul>
<li>When dealing with people we aren’t dealing with people of logic we are dealing with people of emotion.</li>
<li>Rewarding for good behavior is more productive than criticizing. Criticizing is futile.</li>
<li>Never insult or ridicule anyone. -Abraham Lincoln</li>
<li>Criticisms are like homing pigeons they always return home. Those you condemn will likely condemn you back.</li>
<li>If you want to gather honey, don’t kick over the beehive.</li>
<li>Don’t worry about the snow on your neighbors roof when your doorstep is unclean.</li>
</ul>
<h5>Give honest, sincere appreciation</h5>
<ul>
<li>There is only one way of getting someone to do something, make them want to do it.</li>
<li>Everyone likes a compliment. -Abraham Lincoln</li>
<li>The way to develop the best that is in a person is by appreciation and encouragement. Criticism from superiors kills ambition. -Charles Schwab</li>
<li>Lack of appreciation is why most people leave.</li>
<li>Appreciation is sincere. Flattery is telling people what they want to hear. Forget flattery, always use sincere appreciation.</li>
</ul>
<h5>Arouse in the other person an eager want</h5>
<ul>
<li>The only way to influence other people is to talk about what they want and show them how to get it.</li>
<li>Before you try to influence someone try to ask yourself, "How can I get them to want to do it".</li>
<li>The secret to success is to view things from the other person’s point of view as well as your own -William Ford</li>
<li>Arouse in the other person and eager want. Let them know how what I want will benefit them.</li>
</ul>
<h5>Smile</h5>
<ul>
<li>The expression you wear on your face is more valuable than the cloths you wear.</li>
<li>A smile says I like you or glad to see you. A babies smile has the same effect.</li>
<li>People rarely succeed at anything unless they have fun doing it. He said, one employee chose a small company because their voice on the phone was energetic and friendly.</li>
<li>Most people are as happy as they make their mind up to be. -Abraham Lincoln</li>
</ul>
<h5>Remember that a persons name to them is the sweetest and most important sound in any language</h5>
<p><em>Personal story:</em></p>
<ul>
<li>When I was an independent training consultant the first thing I did at the beginning of each class was write down the names of each student. Then, I'd refer to everyone by name via my hidden "name" cheatsheet. It was amazing how many positive comments I received in my evaluations in regards to this simple principle!</li>
</ul>
<h5>The only way to get the best of an argument is to avoid it</h5>
<ul>
<li>If you argue and win you'll never get your opponents good will. -Abraham Lincoln</li>
<li>If you win the argument you lose it. If you lose the argument you lose it. Always avoid an argument.</li>
<li>When two people always agree one of them is not necessary.</li>
<li>When we disagree we are communicating with someone that has similar interests.</li>
</ul>
<h5>Show respect for the other persons opinions. Never say you are wrong</h5>
<ul>
<li>If you can be right 55% of the time you can make a million dollars a day on Wall street. If you are only right 55% of the time how can you tell me I am wrong.</li>
<li>Be wiser than other people if you can but do not tell them so. -Lord Chesterfield</li>
<li>Never tell anyone they are wrong even if you know they are.</li>
</ul>
<h5>If you’re wrong admit it quickly and emphatically</h5>
<ul>
<li>Better to be told you’re wrong by yourself than from someone else.</li>
</ul>
<h5>Try honestly to see things from the other persons point of view</h5>
<ul>
<li>Try to honestly put yourself in the other persons shoes.</li>
<li>Ask why he or she would want to do it - this is thinking from their point of view.</li>
<li>For example, instead of yelling at boys for building an illegal campfire start by telling stories of your enjoyable campfire stories and then talk about how important it is to put the fire out afterwards. This approach is less threatening and is thinking from the others point of view.</li>
</ul>
<h5>Ask questions instead of giving direct orders</h5>
<ul>
<li>Say "You might consider this", or "What do you think of that"? Gives people a sense of pride.</li>
<li>People are more likely to accept an order if they have a say in the decision.</li>
</ul>
<h5>Give the other person a fine reputation to live up to</h5>
<ul>
<li>Give people a reputation to live up to and they will want to live up to that reputation.</li>
<li>For example, if you tell someone to keep up the great work they'll work hard to maintain that reputation.</li>
</ul>Brad Broulikhttp://www.blogger.com/profile/14248585105589786365noreply@blogger.com0tag:blogger.com,1999:blog-7224548894465589501.post-55441641383351686482019-04-14T16:19:00.002-05:002019-12-04T20:29:10.329-06:00Scaling Agile vs High-Performance Agile<style>
/* ///////////////////////////////////////////////// */
/* custom styles */
/* ///////////////////////////////////////////////// */
html {
font: normal 100%/1.5rem "SF Pro Text", "SF Pro Icons", "Helvetica Neue", "Helvetica", "Arial", sans-serif !important; /* font-size: ~16px, line-height: ~24px; */
}
h1, h4 { margin-bottom:0; }
h1 {
font-size: 1.5rem; /* 24px / 16px */
font-weight: normal;
}
h4 {
font-size: 1.125rem; /* 20px / 16px */
font-weight: bold;
line-height: 1.5rem;
}
p {
font-size: 1rem; /* 16px / 16px */
line-height: 1.5rem!important;
margin-top: .5em;
}
.hljs { background:#F9FAFA!important; }
a { text-decoration: none; }
br.clear { clear:both; }
span.code-highlight1 {
font-style: italic;
color: green;
}
a.button:link, a.button:visited {
display: block;
width: 16em;
padding: 0.2em;
line-height: 1.4;
background-color: #94B8E9;
border: 1px solid black;
color: #000;
text-decoration: none;
text-align: center;
}
a.button:hover {
background-color: #369;
color: #fff;
}
table {
margin-left:2px;
margin-right:2px;
border-collapse: collapse;
font-family: "Century Gothic", "Gill Sans", Arial, sans-serif;
}
caption {
text-align: center;
}
td,th {
border: 1px solid #73afb7;
padding: 3px 5px 2px 5px;
}
th {
background:#93C2C8 repeat;
color: white;
text-align: left;
border-color: #14556b;
}
tr.alt td {
background:#F6F8EE repeat;
}
/* ///////////////////////////////////////////////// */
/* End custom styles */
/* ///////////////////////////////////////////////// */
</style>
<link href='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.9.0/styles/vs.min.css' rel='stylesheet'/>
<script src='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.9.0/highlight.min.js'></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.9.0/languages/swift.min.js'></script>
<script>hljs.initHighlightingOnLoad();</script>
<figure style="float: right;"><img alt="vasa" border="1" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjufmn5ubqqVkseMNJKJ0-VTqaqv2LljycKg_GDGVj7LDTSOfAlKg1VkJ9azySMxRJZlvDnghMl5eBUrUK_M8CX2fqBXBeHKqVZnvYRUxTq5xUmzgd_OOyVtyoVWxoK_We38blnDram-ckG/s240/vasa1.jpg" /><figcaption style="text-align: left;"><em>Figure 1. Vasa</em></figcaption></figure>
<p>The <a href="https://en.wikipedia.org/wiki/Vasa_(ship)">Vasa</a> (see Figure 1) is one of the biggest architectural failures of all time. The Vasa was designed to be the most powerful armed vessel in the world - two full gun decks with 72 bronze cannons. Surprisingly, in 1628 this massive vessel sank only 1,400 yards into its maiden voyage because it was too top heavy. The Vasa was built for scale and power but had no agility. Structures built for scale or speed require different architectural strategies. Agile teams also require different team structures to achieve either scale or performance. Let's discuss the differences.</p>
<h4>Scaling Agile</h4>
<p>A <a href="http://bradbroulik.blogspot.com/2018/05/team-of-teams.html">Team of Teams</a> strategy (see Figure 2) is an ideal structure for scaling agile. Refer to my 2-part series on Team of Teams to learn more about the <a href="http://bradbroulik.blogspot.com/2018/05/team-of-teams.html">key ingredients</a> for scaling agile and <a href="http://bradbroulik.blogspot.com/2018/06/software-development-team-of-teams.html">several strategies</a> we can implement to build more scalable agile teams.</p>
<figure style="float:clear;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEioTjp2UQ14jbWNQAD73TRTiKvs0TWLIyzBQyZA4wq5ZlI3LZckba-NGRqvA5O8a6BKb2fX6cONjQOZSD4mKEb7KoKDs9dRL8Jugiv8judUPvT57pSzgtWxq1zqAFFGlUG-uRYXjaa4S2QY/s200/team-of-teams-structure.png" width="200" height="131" data-original-width="457" data-original-height="299" alt="Team of Teams structure for scaling agile" /><figcaption style="text-align: left;"><em>Figure 2. Team of Teams structure for scaling agile</em></figcaption></figure>
<h4>High-Performance Agile</h4>
<p>Do you see any short-term limitations that will reduce performance in the Team of Teams diagram? It's the lines that represent communication. For example, would that diagram be easy to maintain if it was a dependency diagram? No, the overall coupling would be very high. That diagram violates the <a href="https://en.wikipedia.org/wiki/Law_of_Demeter">Law of Demeter</a> and as a result becomes less efficient and more difficult to maintain. Am I saying a Team of Teams strategy is wrong? Not at all, the benefits out weight the disadvantages. However, if your organization has a business driver to complete a short-term project in record time our team structures must adapt to gain maximum team performance.</p>
<figure style="float:clear;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFWNe977sv4BF1s4t1gTBhFi_9_Y3Lon5hMWok97gm6ZJy-oYJ6pFTyb7sWLjcc1XCKM5_SczmblzUdmKWXtobe9BhAoO5ekrjh81MtqU57IDtThK36Y_91vDCer3atmUQVPAQFv6INq7c/s200/agile-high-performance1.png" width="200" height="181" data-original-width="352" data-original-height="318" alt="High performance team structure" /><figcaption style="text-align: left;"><em>Figure 3. Decoupled team structure for high-performance agile</em></figcaption></figure>
<p>When speed becomes the <em>#1 business driver</em> for a short-term project a decoupled team structure (See Figure 3) promotes less distractions, more dedicated focus time, and higher quality. Furthermore, the team size is actually scaled down to reduce communication dependencies. To achieve more performance with fewer resources teams often employ higher skilled resources that are capable of handling multiple roles within the team.</p>Brad Broulikhttp://www.blogger.com/profile/14248585105589786365noreply@blogger.com0tag:blogger.com,1999:blog-7224548894465589501.post-72553742074239219352019-03-03T12:02:00.000-06:002019-12-04T20:29:22.581-06:00Running your first Gatling load test in 10 minutes or less<style>
/* ///////////////////////////////////////////////// */
/* custom styles */
/* ///////////////////////////////////////////////// */
html {
font: normal 100%/1.5rem "SF Pro Text", "SF Pro Icons", "Helvetica Neue", "Helvetica", "Arial", sans-serif !important; /* font-size: ~16px, line-height: ~24px; */
}
h1, h4 { margin-bottom:0; }
h1 {
font-size: 1.5rem; /* 24px / 16px */
font-weight: normal;
}
h4 {
font-size: 1.125rem; /* 18px / 16px */
font-weight: bold;
line-height: 1.5rem;
}
h5 {
font-size: 1.0rem; /* 16px / 16px */
font-weight: bold;
line-height: 1.5rem;
}
p, li {
font-size: 1rem; /* 16px / 16px */
line-height: 1.5rem!important;
margin-top: .5em;
}
.hljs { background:#F9FAFA!important; }
a { text-decoration: none; }
br.clear { clear:both; }
span.code-highlight1 {
font-style: italic;
color: green;
}
a.button:link, a.button:visited {
display: block;
width: 16em;
padding: 0.2em;
line-height: 1.4;
background-color: #94B8E9;
border: 1px solid black;
color: #000;
text-decoration: none;
text-align: center;
}
a.button:hover {
background-color: #369;
color: #fff;
}
table {
margin-left:2px;
margin-right:2px;
border-collapse: collapse;
font-family: "Century Gothic", "Gill Sans", Arial, sans-serif;
}
caption {
font-size:1.0rem;
text-align: left;
padding: 4px 0px;
}
td,th {
border: 1px solid #73afb7;
font-size: 1.0rem;
line-height: 1.25rem;
padding: 3px 5px 2px 5px;
}
th {
background:#93C2C8 repeat;
color: white;
text-align: left;
border-color: #14556b;
}
tr.alt td {
background:#F6F8EE repeat;
}
.table-notes {
display: none;
}
/* Desktop CSS Overrides */
@media (min-width: 35em) {
.traditional-cars
clear: both;
}
/*
.table-notes {
display: block;
}*/
}
/* ///////////////////////////////////////////////// */
/* End custom styles */
/* ///////////////////////////////////////////////// */
</style>
<link href='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.9.0/styles/vs.min.css' rel='stylesheet'/>
<script src='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.9.0/highlight.min.js'></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.9.0/languages/swift.min.js'></script>
<script>hljs.initHighlightingOnLoad();</script>
<a href="#" style="float: right;"><img alt="gatling logo" border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEguuA9srP6tIi5w7ikCfSalcgfPszT0Hn-777mXLrREjkIvyXb17F3w1WZ694xI9v7iVvPDoVjpG2dNr_49ovD1tWZ_ren5exax0ZgXgusfX1qR3DJ_Q__rT9eUK_3ih-CxhX7iYCBJrbh1/s200/gatling-load-testing-image.jpeg" width="200" height="63" alt="Gatling report"/></a>
<p>How many concurrent users can your infrastructure handle? Do you know when your most critical features have regressed in performance? Will your website survive a Denial of Service attack? If you are unsure, <a href="https://gatling.io/">Gatling's</a> lightweight and powerful capabilities will help answer these questions.</p>
<h4>Advantages of Gatling</h4>
<p></p>
<ul>
<li><a href="https://gatling.io/">Gatling</a> is load testing as code which is ideal for integration with DevOps, Continuous Integration, and <a href="https://gatling.io/download/">build tools</a>.</li>
<li>Simulate thousands of requests per second against your APIs and applications.</li>
<li>Identify or troubleshoot performance and load issues.</li>
<li>Are your APIs and website <a href="https://en.wikipedia.org/wiki/C10k_problem">C10k</a> compliant? Determine how many concurrent users your infrastructure, APIs, and applications can handle.</li>
<li>Test your public APIs and applications against <a href="https://en.wikipedia.org/wiki/Denial-of-service_attack">Denial of Service</a> attacks.</li>
<li>Automatically generates an exhaustive, dynamic, and colorful report with high-precision metrics.</li>
<li>Performance is measured in <a href="https://www.dynatrace.com/news/blog/why-averages-suck-and-percentiles-are-great/">percentiles</a> which is more reliable than averages.</li>
</ul>
<h4>Gatling Installation (5 minutes)</h4>
<p>Gatling has several <a href="https://gatling.io/docs/current/installation/">installation options</a>:</p>
<ul>
<li><a href="https://gatling.io/docs/current/installation/#using-the-bundle">Download Gatling's bundle</a> and <a href="https://gatling.io/docs/current/quickstart/#running-gatling">run Gatling</a> locally as a standalone tool (recommended).</li>
<li>Install and run Gatling via a <a href="https://gatling.io/docs/current/installation/#using-a-build-tool">build tool</a>.</li>
<li>Install and run Gatling <a href="https://gatling.io/docs/current/installation/#using-an-ide">using an IDE</a>.</li>
</ul>
<h4>Gatling Directory Structure</h4>
<p>Source code directory:</p>
<ul>
<li><em>$GATLING_HOME/user-files/simulations</em> (directory for your Scala load tests)</li>
<li><em>$GATLING_HOME/user-files/resources</em> (directory for your users.csv file if you need a load test with multiple unique authenticated users)</li>
</ul>
<h4>Running your first load test (5 minutes)</h4>
<ol>
<li>Copy/Merge the <em>user-files</em> directory from my <a href="https://github.com/BradBroulik/gatling-load-tests">GitHub repo</a> to your local <em>$GATLING_HOME</em> directory. The <em>$GATLING_HOME</em> directory will be your Gatling download directory. Refer to the <em><a href="https://github.com/BradBroulik/gatling-load-tests/tree/master/user-files/simulations">user-files/simulations</a></em> directory for tests.</li>
<li><a href="https://gatling.io/docs/current/quickstart/#running-gatling">Run Gatling</a> locally as a standalone application by running the following command:</li>
<ul>
<li>On Linux/Unix: <em>$GATLING_HOME/bin/gatling.sh</em></li>
<li>On Windows: <em>%GATLING_HOME%\bin\gatling.bat</em></li>
</ul>
<p>Gatling will prompt a few questions:</p>
<ul>
<li>Which test to run? Choose any test simulation to run.</li>
<li>Enter an optional test description? Hit enter to skip.</li>
<li>When the simulation is done, the console will display a link to the HTML reports.</li>
</ul>
</ol>
<h4>Load Test Examples</h4>
<p>Refer to the <em><a href="https://github.com/BradBroulik/gatling-load-tests/tree/master/user-files/simulations">user-files/simulations</a></em> directory for test examples. I have a few authenticated and unauthenticated load tests for REST APIs and a website. The authenticated tests refer to user data (<a href="https://github.com/BradBroulik/gatling-load-tests/blob/master/user-files/resources/users.csv">users.csv</a>). The Gatling installation also has several load tests for Web applications (refer to tests in <em>$GATLING_HOME/user-files/simulations/computerdatabase)</em>.</p>
<ul>
<li><a href="https://github.com/BradBroulik/gatling-load-tests/blob/master/user-files/simulations/api/api-itunes-authenticated-test.scala">REST API load test (authenticated)</a></li>
<li><a href="https://github.com/BradBroulik/gatling-load-tests/blob/master/user-files/simulations/web/web-twitter-authenticated-test.scala">Web login load test (authenticated)</a></li>
</ul>
<script src="https://gist.github.com/BradBroulik/4316548be9b469e87c323e8ef31e04d9.js"></script>
<h4>Reports</h4>
<p>Load test reports are automatically generated in <em>$GATLING_HOME/results</em> when your load test completes. Refer to the console when your test finishes for the full path. Gatling creates beautiful charts!</p>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhhe2kaiCL6-w_4-qGU3hZEU6SmBv0DD-ku9u3rwOiIqj-U7fS5IY7ojIV4ErnPUJOG-rvpVufWKlMnDwKRNXOyOVNvHbWwZJxeKIDtRu04k1hA59-Nb7bYXTiFAP8F0YZGdVKvj2fOOAeY/s1600/gatling-report.png" imageanchor="1" ><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhhe2kaiCL6-w_4-qGU3hZEU6SmBv0DD-ku9u3rwOiIqj-U7fS5IY7ojIV4ErnPUJOG-rvpVufWKlMnDwKRNXOyOVNvHbWwZJxeKIDtRu04k1hA59-Nb7bYXTiFAP8F0YZGdVKvj2fOOAeY/s400/gatling-report.png" alt="Gatling report" width="400" height="339" data-original-width="1153" data-original-height="976" /></a>
Brad Broulikhttp://www.blogger.com/profile/14248585105589786365noreply@blogger.com0tag:blogger.com,1999:blog-7224548894465589501.post-38924577935156335692018-10-07T14:32:00.001-05:002019-12-04T20:29:35.106-06:00Feature Flags<style>
/* ///////////////////////////////////////////////// */
/* custom styles */
/* ///////////////////////////////////////////////// */
html {
font: normal 100%/1.5rem "SF Pro Text", "SF Pro Icons", "Helvetica Neue", "Helvetica", "Arial", sans-serif !important; /* font-size: ~16px, line-height: ~24px; */
}
h1, h4 { margin-bottom:0; }
h1 {
font-size: 1.5rem; /* 24px / 16px */
font-weight: normal;
}
h4 {
font-size: 1.125rem; /* 18px / 16px */
font-weight: bold;
line-height: 1.5rem;
}
h5 {
font-size: 1.0rem; /* 16px / 16px */
font-weight: bold;
line-height: 1.5rem;
}
p, li {
font-size: 1rem; /* 16px / 16px */
line-height: 1.5rem!important;
margin-top: .5em;
}
li {
margin-top: 0em!important;
}
.hljs { background:#F9FAFA!important; }
a { text-decoration: none; }
br.clear { clear:both; }
span.code-highlight1 {
font-style: italic;
color: green;
}
a.button:link, a.button:visited {
display: block;
width: 16em;
padding: 0.2em;
line-height: 1.4;
background-color: #94B8E9;
border: 1px solid black;
color: #000;
text-decoration: none;
text-align: center;
}
a.button:hover {
background-color: #369;
color: #fff;
}
table {
margin-left:2px;
margin-right:2px;
border-collapse: collapse;
font-family: "Century Gothic", "Gill Sans", Arial, sans-serif;
}
caption {
font-size:1.0rem;
text-align: left;
padding: 4px 0px;
}
td,th {
border: 1px solid #73afb7;
font-size: 1.0rem;
line-height: 1.25rem;
padding: 3px 5px 2px 5px;
}
th {
background:#93C2C8 repeat;
color: white;
text-align: left;
border-color: #14556b;
}
tr.alt td {
background:#F6F8EE repeat;
}
.table-notes {
display: none;
}
/* Desktop CSS Overrides */
@media (min-width: 35em) {
.traditional-cars
clear: both;
}
/*
.table-notes {
display: block;
}*/
}
/* ///////////////////////////////////////////////// */
/* End custom styles */
/* ///////////////////////////////////////////////// */
</style>
<p>
Feature flags are ways to control the full lifecycle of your features. They allow you to manage components and minimize risk. You can do pretty cool things like roll out features to certain users, exclude groups from seeing a feature, A/B test, and much more. Basically, deploy when you want and release when you’re ready. In this In this <a href="https://speakerdeck.com/bbroulik/feature-flags">presentation</a> we'll discuss the following topics:</p>
<ul style="font-size: 1rem; line-height: 1rem!important;">
<li>What’s a feature flag</li>
<li>Why feature flags</li>
<li>Use cases:</li>
<ul>
<li>Enable/Disable Features at Runtime</li>
<li>Canary Release (dark launch)</li>
<li>Incremental Rollouts (dark launch)</li>
<li>Calendar-driven Launches</li>
<li>A/B Testing</li>
<li>Avoid Feature Branching</li>
</ul>
<li>Disadvantages</li>
<li>Alternative solutions</li>
</ul>
<h3>Slide Deck Sneak Peek</h3>
<figure style="float:left;">
<img border="0" width="200px" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhetTIaaM39DhOVVk2KyCmAQKM9rVJ-FxLp9oWLLJe9UFVxJASQHSjCoHGGpY8CsJZpazLozrBdjTVEaqhorxV5gZhKnDLHtyBT0FuUMsB3NtfMosm4eqb7gSVnGRMU-uEZ1ShkfAsgzjNw/s1600/1.png" alt="Canary Release (dark launch)" />
<figcaption style="text-align: center;"><em>Canary Release (dark launch)</em></figcaption>
</figure>
<figure style="float:left;">
<img border="0" width="200px" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh6l64ZM97PhkE0Sawa5ajx_JXZVtuSH380KTDVB9tco5ziL6kxE_PT6CwSP-JjsU3I4uvFqK_QyftyyfJTYRDUB0it-tWBpjISlbeIwE9mZlXqPw0x0FFFPLhT1zzBOzMNTZ6aUvPRiG36/s1600/2.png" alt="Incremental Rollouts (dark launch)" />
<figcaption style="text-align: center;"><em>Incremental Rollouts (dark launch)</em></figcaption>
</figure>
<p style="clear: both;">
<figure style="float:left;">
<img border="0" width="200px" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg3lVjwmL1NXTv8vYfAptu4kEjpJ3w8HWkigfMR6HzDyRRmG9h9BjGcULgmZyLyIcrFArJu8fa3nq2w4ysezgsSBfgabABlrAdlC_Ky1sqAMqzW20MY3J_o6k10OSVQ9ob-9SfIbweHoU7z/s1600/3.png" alt="Calendar-driven Launches" />
<figcaption style="text-align: center;"><em>Calendar-driven Launches</em></figcaption>
</figure>
<figure style="float:left;">
<img border="0" width="200px" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhOWmzWC4iuzXEK_uacRbbAMtQBYpm6Lh6chYCOfFOZirWQcu_kmAYlCCfWnOfA9fsQv0uwALoRSBW1yF8vWdtwh0-JCgOFyMIgve7LD4A25Ps7xSYZUSK0EOEyLbUy4QGHYI3LJynG3ScL/s1600/4.png" alt="A/B Testing"/>
<figcaption style="text-align: center;"><em>A/B Testing</em></figcaption>
</figure>
Brad Broulikhttp://www.blogger.com/profile/14248585105589786365noreply@blogger.com0tag:blogger.com,1999:blog-7224548894465589501.post-42505822248353968852018-06-03T15:31:00.000-05:002019-12-04T20:29:46.228-06:00Software Development Team of Teams<style>
/* ///////////////////////////////////////////////// */
/* custom styles */
/* ///////////////////////////////////////////////// */
html {
font: normal 100%/1.5rem "SF Pro Text", "SF Pro Icons", "Helvetica Neue", "Helvetica", "Arial", sans-serif !important; /* font-size: ~16px, line-height: ~24px; */
}
h1, h4 { margin-bottom:0; }
h1 {
font-size: 1.5rem; /* 24px / 16px */
font-weight: normal;
}
h4 {
font-size: 1.125rem; /* 18px / 16px */
font-weight: bold;
line-height: 1.5rem;
}
h5 {
font-size: 1.0rem; /* 16px / 16px */
font-weight: bold;
line-height: 1.5rem;
}
p, li {
font-size: 1rem; /* 16px / 16px */
line-height: 1.5rem!important;
margin-top: .5em;
}
.hljs { background:#F9FAFA!important; }
a { text-decoration: none; }
br.clear { clear:both; }
span.code-highlight1 {
font-style: italic;
color: green;
}
a.button:link, a.button:visited {
display: block;
width: 16em;
padding: 0.2em;
line-height: 1.4;
background-color: #94B8E9;
border: 1px solid black;
color: #000;
text-decoration: none;
text-align: center;
}
a.button:hover {
background-color: #369;
color: #fff;
}
table {
margin-left:2px;
margin-right:2px;
border-collapse: collapse;
font-family: "Century Gothic", "Gill Sans", Arial, sans-serif;
}
caption {
font-size:1.0rem;
text-align: left;
padding: 4px 0px;
}
td,th {
border: 1px solid #73afb7;
font-size: 1.0rem;
line-height: 1.25rem;
padding: 3px 5px 2px 5px;
}
th {
background:#93C2C8 repeat;
color: white;
text-align: left;
border-color: #14556b;
}
tr.alt td {
background:#F6F8EE repeat;
}
.table-notes {
display: none;
}
/* Desktop CSS Overrides */
@media (min-width: 35em) {
.traditional-cars
clear: both;
}
/*
.table-notes {
display: block;
}*/
}
/* ///////////////////////////////////////////////// */
/* End custom styles */
/* ///////////////////////////////////////////////// */
</style>
<link href='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.9.0/styles/vs.min.css' rel='stylesheet'/>
<script src='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.9.0/highlight.min.js'></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.9.0/languages/swift.min.js'></script>
<script>hljs.initHighlightingOnLoad();</script>
<p>If a Team of Teams methodology can win a war on terrorism imagine the benefits our IT organizations would gain by applying its principles to software development. This article is part 2 in my series on Team of Teams. <a href="http://bradbroulik.blogspot.com/2018/05/team-of-teams.html" target="_blank">Part 1</a> highlights General McChrystal's transformation to a Team of Teams and its benefits. Part 2 focuses on strategies we can adopt to help transform IT organizations into software development Team of Teams.</p>
<p>Which intersection below most closely resembles a Team of Teams? A free flowing intersection with self-driving cars (see Figure 1) or a traffic light controlled intersection with traditional cars (see Figure 2)?</p>
<a href="https://www.zmescience.com/research/technology/self-driving-cars-billions-revenue-654234/" target="_blank" style="float:left;"><figure style="float:left;"><iframe src="https://giphy.com/embed/l0Ex7BYYtv4NXR6z6" width="240" height="180" frameBorder="0" class="giphy-embed" allowFullScreen></iframe><figcaption style="text-align:left;"><em>Figure 1. Self-driving cars</em></figcaption>
</figure></a>
<a class="traditional-cars" href="https://www.zmescience.com/research/technology/self-driving-cars-billions-revenue-654234/" target="_blank" style="float:left;"><figure style="float:right;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgOP7HpoH4-9nWMWZdguOFzOORzdwejSVr5Cnhsa4aFB9D28C5fSVJdlNhPAHbQdrf4UoYvJbI3DV-xo4lXih9t2n_DoA2oXTmVqK_LR23arMXHHjjhw8DERlsUfvOHwWjw09zSWhFwuwFf/s1600/traditional-cars.gif" width="240" height="180" alt="Traditional cars" /><figcaption style="text-align:left;"><em>Figure 2. Traditional cars</em></figcaption>
</figure></a>
<p> </p>
<table>
<caption>Comparing self-driving vs traditional cars from a Team of Teams perspective</caption>
<tr>
<th></th>
<th style="white-space:nowrap; text-align:center;">Self-driving</br>cars</th>
<th style="white-space:nowrap; text-align:center;">Traditional</br>cars</th>
<th class="table-notes" style="text-align:center;">Notes</th>
</tr>
<tr>
<th style="white-space:nowrap;">Shared</br>Consciousness</th>
<td style="text-align:center; color:green;">Yes</td>
<td style="text-align:center; color:red;">Limited</td>
<td class="table-notes">Self-driving cars are constantly communicating their route and speed in realtime. Traditional cars are limited because they can only communicate their route via turn signals.</td>
</tr>
<tr>
<th style="white-space:nowrap;">Trust</th>
<td style="text-align:center; color:green;">Yes</td>
<td style="text-align:center; color:red;">No</td>
<td class="table-notes">Self-driving cars have trust because of their constant sharing of accurate information. It's difficult to trust traditional cars because they are unpredictable and have limited information sharing.</td>
</tr>
<tr>
<th style="white-space:nowrap;">Empowerment</th>
<td style="text-align:center; color:green;">Yes</td>
<td style="text-align:center; color:red;">No</td>
<td class="table-notes">Self-driving cars are empowered to proceed full-speed through the intersection because of their shared communication and trust. Traditional cars must wait for a green light (command structure) to proceed due of their limited trust and information sharing.</td>
</tr>
</table>
<br/>
<h4>Why Software Development Team of Teams?</h4>
<p>How efficient is your team able to release a small task to production? After adopting a Team of Teams methodology the General’s teams were able to go from intel to strike within an hour. Their efficiency gains allowed them to execute 17 times more raids per month. Our goal should be the same - from requirement to release within a few hours resulting in 17 times more releases a month. Let's review the top strategies we can apply to build stronger and more efficient software development Team of Teams.
<h4>Bake craftsmanship (continuous improvement) into the process</h4>
<p>Do you think the General's teams didn't train, condition or workout? It's part of their lifestyle and built into their process. I classify craftsmanship in two categories: training and refactoring (enhancements, defects). The <a href="https://www.amazon.com/Phoenix-Project-DevOps-Helping-Business/dp/1942788290/ref=sr_1_1?ie=UTF8&qid=1525954448&sr=8-1&keywords=the+phoenix+project" target="_blank">The Phoenix Project</a> <small>( p229)</small> recommended an allocation of 20% for continuous improvement activities. Create a backlog exclusively for these types of tasks and empower your teams to continuously improve their skills, process, and software.</p>
<h4>Reduce batch size</h4>
<p>When the task force launched a mission their goals were small, precision strikes. Small strikes improved their frequency (10 strikes a day). If your teams are fortunate enough to have the efficiency of a CI/CD pipeline then the only limiting factor to daily releases is batch size. Try releasing at least every iteration or more often (daily). Frequent releases promote speed to market, reduce risk, are simpler to manage, and produce quick wins building team morale. Teams must be empowered to reduce batch size. CI/CD pipelines provide little value if releases are monolithic.</p>
<h4>"Power up" to improve team health</h4>
<p>Have you ever played <a href="https://en.wikipedia.org/wiki/Fortnite" target="_blank">Fortnite</a>? Many of the team strategies applied in this battle royale game apply well to software development. One strategy in particular is the “power up” strategy or boosting your <a href="http://www.ign.com/wikis/fortnite/Battle_Royale_Healing_and_Consumables" target=_blank">teammates health</a> when they are running low. On a software development team we can boost the health of the overall team by information sharing and paired programming. The most effective team I have been on was a client project in 2003. This project had around 30 developers and we were required to pair program 100%. Pairs needed to rotate drivers and a new partner was required for the the morning and afternoon sessions. Software quality was very high and knowledge sharing with a new partner was an effective way to "power up" the resources across the team. Paired programming improves craftsmanship (continuous improvement), reduces defects, improves information sharing, and builds trust.
<h4>Transparent leadership</h4>
<p>Having a transparent leader that posts frequently on Twitter isn't all bad. Has our president read Team of Teams? General McChrystal said his transparency (all calls on speakerphone and O&I briefings with thousands) saved many lives. In addition, his transparency greatly improved trust and shared consciousness across all teams. Perhaps we should try the same techniques. Would transparency and trust improve if we removed all cubicle walls, removed all office doors, replaced all phones with speakerphones, and made everyones calendar transparent?</p>
<h4>Build a liaison program</h4>
<p>A liaison program was General McCrystal's primary strategy for building trust across his teams. From a development perspective, liaison programs are opportunities to join other teams for short-term projects or assignments. In addition to improving trust these opportunities also expose developers to alternative coding practices, techniques, and team dynamics. Furthermore, <a href="https://en.wikipedia.org/wiki/Who_Moved_My_Cheese%3F" target="_blank">moving cheese</a> or trying <a href="https://en.wikipedia.org/wiki/Who_Moved_My_Cheese%3F" target="_blank">new cheese</a> often sparks new learning and growth opportunities from a team and individual perspective. Teams without a mature shared consciousness will be very resistant to this type of change highlighting a flaw within their own team. Anyone adopting new cloud or platform technologies? What better way to learn than actually join their teams for several iterations.</p>
<h4>Continuous gardening</h4>
<p>Managers have the most important role in regards to tending our Team of Teams. In particular, they need to provide water, fertilizer, and weed control for the resources across the teams:
<ul>
<li>Water - growing trust and transparency. Water is the most important ingredient. A garden will not survive without water and a Team of Teams will not exist without trust and transparency.</li>
<li>Fertilizer - building "power up" opportunities for resources to learn and grow. Resources won't reach their full potential if they are not given the opportunity to grow and learn. In addition, incentivize developers to attend conferences or <a href="https://www.meetup.com/" target="_blank">meetups</a>, watch <a href="https://dzone.com/articles/12-java-youtube-channels-you-should-follow-in-2018" target="_blank">YouTube developer channels</a>, or contribute to <a href="https://github.com/" target="_blank">open source</a>.</li>
<li>Weed control - eliminate rubber stamps and command structures that reduce team productivity. Without weeds teams will become more empowered and efficient.</li>
</ul>
<h4>Build an empowerment radar</h4>
<p>Does your team have high, medium or low empowerment? Document rubber stamps, command structure decisions, and shared consciousness limitations that impede a free flowing software development life cycle. Then, tend the garden to remedy those limitations. Remember, empowerment fuels motivation - an individual or team who makes a decision becomes more invested in its outcome. The goal is to eliminate these impediments so your team can become as efficient as self-driving cars.</p>
Brad Broulikhttp://www.blogger.com/profile/14248585105589786365noreply@blogger.com0tag:blogger.com,1999:blog-7224548894465589501.post-75471432981867446912018-05-20T09:40:00.000-05:002019-12-04T20:29:58.151-06:00Team of Teams<style>
/* ///////////////////////////////////////////////// */
/* custom styles */
/* ///////////////////////////////////////////////// */
html {
font: normal 100%/1.5rem "SF Pro Text", "SF Pro Icons", "Helvetica Neue", "Helvetica", "Arial", sans-serif !important; /* font-size: ~16px, line-height: ~24px; */
}
h1, h4 { margin-bottom:0; }
h1 {
font-size: 1.5rem; /* 24px / 16px */
font-weight: normal;
}
h4 {
font-size: 1.125rem; /* 18px / 16px */
font-weight: bold;
line-height: 1.5rem;
}
h5 {
font-size: 1.0rem; /* 16px / 16px */
font-weight: bold;
line-height: 1.5rem;
}
p, li {
font-size: 1rem; /* 16px / 16px */
line-height: 1.5rem!important;
margin-top: .5em;
}
.hljs { background:#F9FAFA!important; }
a { text-decoration: none; }
br.clear { clear:both; }
span.code-highlight1 {
font-style: italic;
color: green;
}
a.button:link, a.button:visited {
display: block;
width: 16em;
padding: 0.2em;
line-height: 1.4;
background-color: #94B8E9;
border: 1px solid black;
color: #000;
text-decoration: none;
text-align: center;
}
a.button:hover {
background-color: #369;
color: #fff;
}
table {
margin-left:2px;
margin-right:2px;
border-collapse: collapse;
font-family: "Century Gothic", "Gill Sans", Arial, sans-serif;
}
caption {
text-align: center;
}
td,th {
border: 1px solid #73afb7;
padding: 3px 5px 2px 5px;
}
th {
background:#93C2C8 repeat;
color: white;
text-align: left;
border-color: #14556b;
}
tr.alt td {
background:#F6F8EE repeat;
}
/* ///////////////////////////////////////////////// */
/* End custom styles */
/* ///////////////////////////////////////////////// */
</style>
<link href='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.9.0/styles/vs.min.css' rel='stylesheet'/>
<script src='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.9.0/highlight.min.js'></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.9.0/languages/swift.min.js'></script>
<script>hljs.initHighlightingOnLoad();</script>
<a href="#" style="float: right;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-4IDIT43iwAoeODKmgvwN_-c_BiCaq7GNNJKbYUb1lEFXdj0LnoMxmc-sD0VXQkJkZUI6JUti5Jcr_lBg4AObSr79zHTbqx0JzCAtPwOTxEXw__00Zf7qJmrY8WlMvJNLMWpXfKTx3Pyi/s1600/team-of-teams.jpeg" data-original-width="150" data-original-height="167" /></a><p>A <a href="https://www.amazon.com/Team-Teams-Rules-Engagement-Complex/dp/1591847486" target="_blank">Team of Teams</a> is, <em>"An organization within which the relationships between constituent teams resembled those between individuals on a single team: teams that had traditionally resided in separate silos would now have to become fused to one another via trust and purpose”</em> <small>(p132)</small>. To win the war on terrorism General McChrystal was quick to realize their traditional management style and team dynamics (see Figure 1) needed to adapt to keep pace with the more agile and adaptive Al Qaeda enemy in Iraq. This article is part 1 in my series of Team of Teams. Part 1 highlights General McChrystal's transformation to a Team of Teams and its benefits. <a href="http://bradbroulik.blogspot.com/2018/06/software-development-team-of-teams.html" target="_blank">Part 2</a> focuses on strategies we can adopt to transform IT organizations into Software Development Team of Teams.</p>
<figure style="float:clear;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgzu5cpRUxA5KmzeklreTkc5GVK2AupF4PHSyiKrlMvB8i9XbR_M8sY7O2jttLJdPUcJEm6U1PJaDlapzWKdMDOZ7gMdb6EkDLuxXy1bYfhMIbbFlDDTWWNnDz1jkyfcEU6so5PkSC1eqcW/s1600/team-of-teams-pic.jpeg" data-original-width="500" data-original-height="563" alt="Team of Teams structure" /><figcaption style="text-align: left;"><em>Figure 1. From Command to Team of Teams</em></figcaption>
</figure>
<h4>Why Team of Teams?</h4>
<p>General McChrystal's teams (SEALs, Army Special Forces, Rangers) were able to execute operations 17 times faster. With their old command structure they managed 18 raids per month; with a Team of Teams they preformed 300 raids a month. These efficiency gains were the direct result of their improved information sharing, trust, and empowerment resulting in more effective decision making.</p>
<h4 style="clear:both">The Ingredients</h4>
<p>A Team of Teams strategy is built upon three key ingredients: shared consciousness, trust, and empowerment (see Figure 3). Let’s review each and highlight the implementation strategies General McChrystal’s adopted to transform an organization of thousands across 70 remote locations.</p>
<figure style="float:clear;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjiXb4mZNYxf_-t329Wch4IYlzpIT7v8JydVkQNQYjobZvPuArLTZ7x-XhOa5J_Cg4D1fElLUSlnlj00ovTYrAgsDY5gIHKUhnXdndAqcFlM7fPZ7cdwxhKmK47k9JdRJiw9idK4m8oXWQC/s1600/team-of-teams-ingredients.jpeg" data-original-width="400" data-original-height="336" alt="Team of Teams ingredients" /><figcaption style="text-align: left;"><em>Figure 3. Team of Teams ingredients</em></figcaption>
</figure>
<h4 style="clear:both">Shared Consciousness</h4>
<p>This ingredient is a generalized awareness about what other teams are doing and sharing information. The book reviewed two tragic examples that were direct results of failed shared consciousness - the <a href="https://www.amazon.com/Secret-Apollo-Management-American-European/dp/0801885426" target="_blank">Apollo project</a> and <a href="http://www.autonews.com/article/20140529/OEM02/140529848/barra-says-recall-crisis-stemmed-from-gms-communication-breakdowns" target="_blank">GMs faulty ignition switch</a>. Both examples highlight the importance of shared consciousness on critical projects.</p><p>How much time should we allocate to information sharing across teams? The best answer I found was this quote pertaining to the Apollo project, <em>"Specialists continued to do specialized work, but they needed an understanding of the project as a whole, even if establishing that understanding took time away from other duties and was, in some ways, “inefficient”. NASA leadership understood that, when creating an interactive product, confining specialists to a silo was stupid: high-level success depended on low-level inefficiencies. Even the engineers most ardently opposed to systems management found that many technical problems could be solved only by sharing information"</em> <small>(p149)</small>.</p><p>Here are a few strategies General McCrystal applied to improve their shared consciousness:</p>
<ul>
<li>They moved teams to open workspaces <small>(p159)</small>.</li>
<li>General McCrystal never used his private office, instead he sat in their open workspace and took all calls on speakerphone for everyone to hear <small>(p163)</small>.</li>
<li>They had a daily O&I (operations and intelligence) briefing that was the core of their transformation. This meeting pumped information about the entire scope of operations to all members of the Task Force and partner agencies. It also offered everyone the chance to contribute. The O&I was a hour and a half video conference across seventy remote locations with thousands of people <small>(p171, p227-228)</small>.</li>
<li>They didn't want all teams to become generalists. Rather, they wanted to fuse generalized awareness with specialized expertise <small>(p153)</small>.</li>
</ul>
<h4>Trust</h4>
<p>Trusted relationships across teams is a positive enabler for information sharing and promotes team empowerment. Trust is the <em>most important</em> ingredient of the three. Without trust shared consciousness and empowerment will not exist.</p><p>Here are a few strategies General McCrystal applied to improve their trust:</p>
<ul>
<li>To build trust between the different teams (SEALS, Army Rangers, Army Special Forces) they took one member from each team and embedded them on the others for 6 months. Each group was incentivized to send their best resource because they were representing their organization. There was great resistance to this but as a result the teams built stronger connections <small>(p176)</small>.</li>
<li>More than directing, leaders must exhibit personal transparency. Team members must be allowed to monitor the leader. This is the new ideal <small>(p232)</small>. The General's transparency was demonstrated during their O&I briefings, open workspace participation, and broadcasting calls on speakerphone.</li>
</ul>
<h4>Empowerment</h4>
<p>Empowerment is when leadership gives the team approval in decision making. When Task Force units (SEALS, Army Special Forces, Rangers) would wake up General McChrystal for air strike approval he’d always ask if they wanted him to approve the strike. Life or death decisions confirmed the role as an important leader but the General often questioned his value in the process because he’d rarely have groundbreaking insight. His approval was a rubber stamp that slowed the process.</p><p>Here are a few strategies General McCrystal applied to enable empowerment:</p>
<ul>
<li>With shared information teams could <em>“do the right thing”</em> rather than <em>“do things right”</em>. People at every level of the organization had the information and connectivity to determine what the right thing was, in real time. But, held back by their internal processes, they lacked the ability to act on that determination <small>(p203)</small>.</li>
<li>The General was always ultimately responsible and more often than not those below him reached the same conclusion. This way their team would be empowered to do what was needed <small>(p209)</small>.</li>
<li>Eventually a rule of thumb emerged: "If something supports our effort, as long as it is not immoral or illegal," you could do it <small>(p214)</small>.</li>
<li>An individual or team who makes a decision becomes more invested in its outcome <small>(p215)</small>.</li>
</ul>
<h4>Sustainable Garden</h4>
<p>With our three key ingredients in hand, the most important step is growing and maintaining the sustainable garden (see Figure 4). This becomes the primary responsibility for leaders. General McChrystal spent most of his efforts growing and maintaining his Team of Teams. Without constant watering, fertilizing, and weeding the garden becomes unsustainable and breaks down.</p>
<figure style="float:clear;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEihNQ-6YYwGanwxSFyB8aEopnn0_8VNpRHBrHkTLcTs-rtBNzgojp5bNifdab9SY258mpzyxStXgCApsCK9R2vNznq7RXunCX3VBB5zgIVTzPx-l3p_T2j7LWyEkerDgFfS9Fw7GrUMpinY/s1600/sustainable-garden.jpeg" data-original-width="400" data-original-height="311" alt="Sustainable garden" /><figcaption style="text-align: left;"><em>Figure 4. Leaders tend the sustainable garden</em></figcaption>
</figure>
<br/>
<p>With three key ingredients, cultural change, transparent leadership and continuous gardening the General's Task Force transformed itself into an organic entity that dramatically improved their speed and precision. In part 2, we'll focuses on strategies we can adopt to transform IT organizations into Software Development Team of Teams.</p>
Brad Broulikhttp://www.blogger.com/profile/14248585105589786365noreply@blogger.com0tag:blogger.com,1999:blog-7224548894465589501.post-32544998436561638992018-03-19T17:49:00.000-05:002019-12-04T20:30:08.724-06:00App Reviews - From OK to Great in 12 days<style>
/* ///////////////////////////////////////////////// */
/* custom styles */
/* ///////////////////////////////////////////////// */
html {
font: normal 100%/1.5rem "SF Pro Text", "SF Pro Icons", "Helvetica Neue", "Helvetica", "Arial", sans-serif !important; /* font-size: ~16px, line-height: ~24px; */
}
h1, h4 { margin-bottom:0; }
h1 {
font-size: 1.5rem; /* 24px / 16px */
font-weight: normal;
}
h4 {
font-size: 1.125rem; /* 20px / 16px */
font-weight: bold;
line-height: 1.5rem;
}
p {
font-size: 1rem; /* 16px / 16px */
line-height: 1.5rem!important;
margin-top: .5em;
}
ol {
font-size: 1rem; /* 16px / 16px */
line-height: 1.5rem!important;
}
.hljs { background:#F9FAFA!important; }
a { text-decoration: none; }
br.clear { clear:both; }
span.code-highlight1 {
font-style: italic;
color: green;
}
a.button:link, a.button:visited {
display: block;
width: 16em;
padding: 0.2em;
line-height: 1.4;
background-color: #94B8E9;
border: 1px solid black;
color: #000;
text-decoration: none;
text-align: center;
}
a.button:hover {
background-color: #369;
color: #fff;
}
table {
margin-left:2px;
margin-right:2px;
border-collapse: collapse;
font-family: "Century Gothic", "Gill Sans", Arial, sans-serif;
}
caption {
text-align: center;
}
td,th {
border: 1px solid #73afb7;
padding: 3px 5px 2px 5px;
}
th {
background:#93C2C8 repeat;
color: white;
text-align: left;
border-color: #14556b;
}
tr.alt td {
background:#F6F8EE repeat;
}
/* ///////////////////////////////////////////////// */
/* End custom styles */
/* ///////////////////////////////////////////////// */
</style>
<link href='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.9.0/styles/vs.min.css' rel='stylesheet'/>
<script src='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.9.0/highlight.min.js'></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.9.0/languages/swift.min.js'></script>
<script>hljs.initHighlightingOnLoad();</script>
<div style="float: right; padding:0; margin:0; border: 0; margin:0;"><figure style="margin:1px;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiB-0CcTX-SXk9PJJrQrVu83q_X5ZR1HKpxBK0nZ4gCUXljd9YJZfgC_-zUuFsJukc43z6xFVzFw9_quseFhHlOEw-D0c2g0mSSjPMydMsfIYVC5tEUQpUqQDglt2e6p1xDE61ljN3Iby-r/s320/app-review-prompt.jpg" width="180" height="320" />
<figcaption style="text-align: left;"><em>App review prompt</em></figcaption></figure></div>
<p>What are the characteristics of a successful app? Is it revenue, number of downloads, app rating, active user count, or your crash-free rating? While each metric plays a role in overall success the important factor is we have the ability to improve the metrics that are most valuable to us. For example, a primary goal for our <a href="https://itunes.apple.com/us/app/myhp/id562354498?mt=8">HealthPartners app</a> this year was to improve our <em>3.8</em> app rating. Let's explore the strategy we applied at HealthPartners that improved our app rating to <em>4.6</em> in just 12 days. This strategy is actually easier done than said.</p>
<h4>Identify the top 3 'Make Good Happen' moments in your app</h4>
<p><mark>The most important tip for a successful user review is a <em>timely</em> prompt after the user has finished a <em>simple</em> and <em>valuable</em> workflow.</mark> For example, in our <a href="https://itunes.apple.com/us/app/myhp/id562354498?mt=8">HealthPartners app</a> we identified these following workflows as our top 'Make Good Happen' moments:</p>
<ol>
<li>After a user refills a prescription</li>
<li>After a user schedules a clinic appointment</li>
<li>After a user submits an account reimbursement</li>
</ol>
<p>After users complete these simple and valuable workflows they will be more likely to submit a positive app review. According to our iTunes Connect metrics 93% of our reviews have been 4 stars or higher.</p>
<div id="ratings-link" href="#" style="float: none; clear: both; padding: 0; border: 0;">
<figure>
<img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAVJiBlqiAtKuFk-GP81YB3Q6U1leS35_7ViwRw1dgRenHji0lDamuATiSqZYccK0_F7Fje7W00cQENVAOBYOrdLN-fPKWogK9tfP6B271exMa-wA-Ev9KvJT3GtCVP4Ll5giPVLwAZbR9/s320/myhp-ratings.png" width="220" height="204" data-original-width="1237" data-original-height="1146" /><figcaption style="text-align: left;"><em>App ratings</em></figcaption>
</figure></div>
<h4>Trigger app review prompt</h4>
<p>Prompting an app review requires minimal code.</p>
<pre style="white-space: pre-wrap; font-family:arial; font-size:12px; width:99%; height:auto; -webkit-overflow-scrolling:touch; overflow:auto; padding:0px; color:#000000; text-align:left; line-height:20px; border: 1px solid #d6d6d6; background-color:#F9FAFA!important;"><code style="color:#000000; background:#F9FAFA!important;" class="swift">if #available(iOS 10.3, *) {
SKStoreReviewController.requestReview()
}
</code></pre>
<p>Apple released their <a href="https://developer.apple.com/app-store/ratings-and-reviews/">rating and review</a> capabilities in iOS 10.3. Therefore, a version check is necessary if you support a prior iOS version. Otherwise the <em><a href="https://developer.apple.com/documentation/storekit/skstorereviewcontroller">requestReview()</a></em> trigger is quite simple. However, Apple will only trigger a prompt within your shipped app <a href="https://developer.apple.com/app-store/ratings-and-reviews/">3 times a year</a>. Apple's <em>requestReview()</em> algorithm ultimately determines the frequency and timing of the actual prompt.
</p>
<h4>Limit app review prompt to core users (optional)</h4>
<p>Since it's not clear when Apple may display the review prompt I wanted to avoid an app review prompt for first time users. A core user or returning user is more likely to provide positive feedback. Therefore, I limited the review prompt to core users.</p>
<script src="https://gist.github.com/BradBroulik/4f7ec2750c2507c92f3e4ede1e24fcd1.js"></script>
<!--
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEisciHoaNeyyg6GWWSfYswe0VYM9aTcyoK2AJ7ejbbJun-neeK4a9iNRddY1OQmHBgsIt1dOtEual9wjbbhmRKeTJE7ARkwV0BNlwowUybIoLlT3ZXncHOsBtZNrXxNMIt5iZciJds0hmuA/s1600/app-review-custom-code.jpeg" imageanchor="1" >
<img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh0ep9h-4CQeNhHr78uNgXlkvIr1Ceee69Qm5cF9Mb6Aa6dCxn4E4YfcUWi3w1Ay9qa-m8mG12Q1Y0RPFMX51vzZDiOAbTlBkKc4RShyphenhyphen67SR64Ww67Azm_O19dCJ8FausGEC24gvfcgC81C/s1600/app-review-custom-code.jpeg" max-width="100%" width="98%" data-original-width="1600" data-original-height="939" /></a>
-->
<h4>App rating trends mashup</h4>
<p>Appbot recently published an article comparing app rating trends titled, <a href="https://stories.appbot.co/has-ios-11-really-affected-star-ratings-d9122b8c0403">"Has iOS 11 really affected star ratings?"</a>. I performed a mashup and included HealthPartners within their rating diagram to gather a greater perspective of the improvement our app has experienced.</p>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj21dr-hcgPLyQSpbTJijxqcdLVgz3ylw_ywmyRscoXbcS7KcTwNh_3a_Co3z1C4xEpvukWnXeI_JBCPaqToz2uWkw15vclZrHE2_gEtspnMrlelV9swRS59i2jJG94O0B2T7RQGRHItcYb/s1600/app-ratings-mashup.jpeg" imageanchor="1" style="clear: both; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiF3XHCJgtdV078Sv4wlE9XdWYQXTD9Qrn1rUaBu32fz0TbpI1YeVPfFR3BC61xkbvprCp30YD8WYctrT8HwbKvY2epWa8IeKHCLAWGFUHuH2pK7rbAUPACthh7vcyxMcmIzBcAnMtAJapk/s1600/app-ratings-mashup.jpeg" max-width="100%" width="98%" data-original-width="1600" data-original-height="1045" /><figcaption style="text-align: left;"><em>iOS 11 app rating trends</em></figcaption>
</a>
<p>Are these tasks easier done than said? Simply add review prompts to your apps top 3 'Make Good Happen' moments and your ratings will improve dramatically too.</p>Brad Broulikhttp://www.blogger.com/profile/14248585105589786365noreply@blogger.com0tag:blogger.com,1999:blog-7224548894465589501.post-80486437120844145152017-05-20T12:28:00.001-05:002017-05-20T12:32:36.647-05:00 Adaptive and Self-Sizing Subtitle Cell<style>
/* ///////////////////////////////////////////////// */
/* custom styles */
/* ///////////////////////////////////////////////// */
html {
font: normal 100%/1.5rem "SF Pro Text", "SF Pro Icons", "Helvetica Neue", "Helvetica", "Arial", sans-serif !important; /* font-size: ~16px, line-height: ~24px; */
}
h1, h4 { margin-bottom:0; }
h1 {
font-size: 1.5rem; /* 24px / 16px */
font-weight: normal;
}
h4 {
font-size: 1.125rem; /* 20px / 16px */
font-weight: bold;
line-height: 1.5rem;
}
p {
font-size: 1rem; /* 16px / 16px */
line-height: 1.5rem!important;
margin-top: .5em;
}
.hljs { background:#F9FAFA!important; }
a { text-decoration: none; }
br.clear { clear:both; }
span.code-highlight1 {
font-style: italic;
color: green;
}
a.button:link, a.button:visited {
display: block;
width: 16em;
padding: 0.2em;
line-height: 1.4;
background-color: #94B8E9;
border: 1px solid black;
color: #000;
text-decoration: none;
text-align: center;
}
a.button:hover {
background-color: #369;
color: #fff;
}
table {
margin-left:2px;
margin-right:2px;
border-collapse: collapse;
font-family: "Century Gothic", "Gill Sans", Arial, sans-serif;
}
caption {
text-align: center;
}
td,th {
border: 1px solid #73afb7;
padding: 3px 5px 2px 5px;
}
th {
background:#93C2C8 repeat;
color: white;
text-align: left;
border-color: #14556b;
}
tr.alt td {
background:#F6F8EE repeat;
}
/* ///////////////////////////////////////////////// */
/* End custom styles */
/* ///////////////////////////////////////////////// */
</style>
<link href='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.9.0/styles/vs.min.css' rel='stylesheet'/>
<script src='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.9.0/highlight.min.js'></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.9.0/languages/swift.min.js'></script>
<script>hljs.initHighlightingOnLoad();</script>
<a href="#" style="float: right;"><img alt="fat free ice cream" border="1" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi1-AKigEAoNn_lmyvadsNNeVajKtqsOMbQzBAULFZhzVqL2MYQZ9Gw56EnRlFuhsTUA9-9Nq9HamF98ASsH2WbEoz8ptkzK29JkY8WlTzdL30OBJ6n6Us46BbLhccBNrjUSviF3reBtkWM/s320/fatfree.png" /></a>
<p>Imagine eating all the ice cream you ever wanted and not gaining a pound. There's a classic <a href="https://www.youtube.com/watch?v=4rRQbvdnJFU">Seinfeld episode</a> about this dream and they eventually learned it was too good to be true. If you've ever used Apple's <a href="https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/TableView_iPhone/TableViewStyles/TableViewCharacteristics.html#//apple_ref/doc/uid/TP40007451-CH3-SW14">subtitle cell</a> you may have experienced similar disappointment. The default subtitle cell is adaptive but it does not support self sizing and therefore it is not accessible. If accessibility is a requirement on your projects then the only option is to create a custom Subtitle cell that supports both adaptability and self-sizing.</p>
<h4>Adaptive & Self-Sizing Custom Subtitle Cell</h4>
<p>An ideal subtitle cell should be both adaptive and self-sizing (see Figure 1). As shown, this custom subtitle cell adapts to support many different configurations. Most importantly, it supports self-sizing - the cell automatically grows to fit the available content and it will grow based on font size adjustments. Now that our cell is also accessible we can certify it <em>fat free</em>!</p>
<figure style="float:clear;">
<img border="0" max-width="100%" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEigJWnqC5j_dP7_XoRGEqUl6OwglXEfAdrRKKYYzUQR9RjxBCbYWfxn9i1JZReNApE3dgKMs9MO9Wdjjk3a1uS_9-oNzvRYPs-EzGvqhSYNnoMO7TnaYqoLHu9vqjrLaw0XT-k9lOei0p_E/s640/adaptive-examples.jpeg" alt="Adaptive and self-sizing subtitle cell examples" />
<figcaption style="text-align: left;"><em>Figure 1. Adaptive and self-sizing subtitle cell examples</em></figcaption>
</figure>
<h4>Default Subtitle Cell</h4>
<p>In comparison, the default subtitle cell when loaded with the exact same content as shown previously is not accessible (see Figure 2). Unfortunately, this option doesn't pass the fat free certification test.</p>
<figure style="float:clear;">
<img border="0" max-width="100%" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhxunkn0-r96vshIB0pge07oyX87vdbRCFLzARmGVfmRB-xuioeZ0QCEw23DnKZ7_560pJD2K_WKqUDWtI4caQcrcOjY1L2vJ4TLH8VnaLZlWJb6n1yUJee-XIXa_ktQsITU4k0W3PspXoV/s580/default-subtitle-cell.jpeg" alt="Default subtitle cell examples" />
<figcaption style="text-align: left;"><em>Figure 2. Non-accessible Default Subtitle Cell</em></figcaption>
</figure>
<h4>Custom Subtitle Cell Layout</h4>
<p>The auto layout configuration for the custom subtitle cell is very lean (see Figure 3). The Stack Views implicitly manage adaptability and the text labels are configured to support self-sizing.</p>
<figure style="float:clear;">
<img border="0" max-width="100%" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEguE4R2yL_rMbGNsDs4ew6T86x2Tp0CEppuNRMY2Xrv_hnoo9VtkINFb3tw1Sg5sePUMJAJ_gy2h4jgLU6yyC0wsiNi77br1YgrOW7HQHvp7azJ0ovpPBTQ904RmzZ7KIRcSoSIn32jYiyK/s580/subtitle-xib.jpeg" alt="Subtitle cell layout" />
<figcaption style="text-align: left;"><em>Figure 3. Custom Subtitle Cell Layout</em></figcaption>
</figure>
<h4>Custom Subtitle Cell Configuration</h4>
<p>The custom subtitle cell will adapt based on its configured cell data (see Figure 4). This <a href="https://github.com/BradBroulik/iOS-self-sizing-subtitle-cell/blob/master/AdaptiveSubtitleCellDemo/SubtitleCell.swift">custom subtitle cell</a> and entire demo is available on my <a href="https://github.com/BradBroulik/iOS-self-sizing-subtitle-cell">GitHub repository</a>.</p>
<a href="https://github.com/BradBroulik/iOS-self-sizing-subtitle-cell/blob/master/AdaptiveSubtitleCellDemo/SubtitleCell.swift">
<figure style="float:clear;">
<img border="0" max-width="100%" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhd6WgewPYLLxpuTqVA3KyVZdDqzDc6bXRh9ZTz6JmE4M8uTAsdBL-8AFbUd_dvY2APJRqfm6RJ5rKgVBb_a8XEm5HfBLif3MW0cWWqCx8BoOek6EjXzXuwhUprqX-LZjfIwTHj7yymy2-f/s640/subtitleCellDataStruct.png" alt="Subtitle cell data" />
<figcaption style="text-align: left;"><em>Figure 4. Subtitle cell data</em></figcaption>
</figure></a>
Brad Broulikhttp://www.blogger.com/profile/14248585105589786365noreply@blogger.com0tag:blogger.com,1999:blog-7224548894465589501.post-30364944688805681272017-04-02T10:05:00.003-05:002017-04-02T10:15:06.812-05:00Swift Performance Tips for Busy iOS Developers - Presentation<p>
Want to learn quick tips that will improve your apps performance and load time? In this <a href="https://speakerdeck.com/bbroulik/swift-performance-tips-for-busy-ios-developers">presentation</a>, we’ll explore the latest Swift techniques we can apply to create faster iOS apps. Topics discussed will include:</p>
<ul style="font-size: 1rem; line-height: 1.25rem!important;">
<li>The three dimensions of performance:</li>
<ul>
<li>Allocation (stack vs heap)</li>
<li>Reference counting (less vs more)</li>
<li>Method dispatch (static vs dynamic)</li>
</ul>
<li>Is Swift faster than Objective-C?</li>
<li>Are structs faster than classes?</li>
<li>Is inheritance faster than protocol extensions?</li>
<li>Grand Central Dispatch patterns</li>
<li>Whole module optimization and more</li>
</ul>
<h3>Source Code and Demos</h3>
<p>Many of my performance tests are available as <a href="https://gist.github.com/BradBroulik/8824476d6c8e35ddd9da7c84982b0496">Gists</a>. Refer to the slides "Gist" Link in the lower left - Enjoy!</p><br/>
<h3>Slide Deck Sneak Peek</h3>
<figure style="float:left;">
<img border="0" width="200px" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQy53t-RjIJwMyyDaqOhNDiqN83AKMR7koNhCQE4qMI6LkY-5f-NhQ2Tc-5W3fBEBSWAIAEgEtq1gsNN-a_wfbcMMnk8fOMJHm4vnqBc5XuRpoXHKtcbDy5xCL4PvNutLr0oPOznhd7MBZ/s1600/38-dimensions-of-performance.jpg" alt="Dimensions of performance" />
<figcaption style="text-align: center;"><em>Dimensions of performance</em></figcaption>
</figure>
<figure style="float:left;">
<img border="0" width="200px" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgWEdEfgp5eYFuU8WGe5FDSXTeQZxChlJQ38yuswWUpu9W1Expftg34RkBJJknp_hJKVep04mKKMf07uMQLq3VuoQ8kM4r96p5nLfNFJCJMb9Q9ug3Ps1EXugOfM3G6nh7r8IBi0Yu42Jml/s1600/37-enable-whole-module-optimization.jpg" alt="Enable Whole Module Optimization" />
<figcaption style="text-align: center;"><em>Enable Whole Module Optimization</em></figcaption>
</figure>
<p style="clear: both;">
<figure style="float:left;">
<img border="0" width="200px" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjEhUaWfCaYtSK4Fe5aTJ1WDb9ka5bkB_CRZJmecR31X4HhjqeBcluOCcj6NFzueCpmLVeZYeRzGJzCI_za02FeB9KeoNZndAdR4pKgv7spmr3jmKiNZGHAmSu0F5FvmDyqbUeZlCJH7cXa/s1600/14-avoid-for-in.jpg" alt="Avoid for-in" />
<figcaption style="text-align: center;"><em>Avoid for-in</em></figcaption>
</figure>
<figure style="float:left;">
<img border="0" width="200px" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjugZcbw4hVjqXhSBLo3e19wmzpy37BMribjvyXv4CsQN5lb1GTnuAb_72C5QtVAwf2xz2_ON9coTic8XIUI15WHdQMoZyqkxearXiLQoWLIX6Gl1lgVPeXD1N-4FlZucuAoP_3FlyS5Ek4/s1600/36-grouping-work-requests.jpg" alt="Grouping work requests"/>
<figcaption style="text-align: center;"><em>Grouping work requests</em></figcaption>
</figure>
<p style="clear: both;">
<p>Looking for even more performance tips? The Swift team has an excellent post with even more <a href="https://github.com/apple/swift/blob/master/docs/OptimizationTips.rst#writing-high-performance-swift-code">Swift Performance Tips.</a></p>
Brad Broulikhttp://www.blogger.com/profile/14248585105589786365noreply@blogger.com0tag:blogger.com,1999:blog-7224548894465589501.post-27168268289302577562017-02-08T18:17:00.000-06:002017-02-08T20:28:05.960-06:00Swift Performance Tips for Busy iOS Developers<style>
/* ///////////////////////////////////////////////// */
/* custom styles */
/* ///////////////////////////////////////////////// */
html {
font: normal 100%/1.5rem "SF Pro Text", "SF Pro Icons", "Helvetica Neue", "Helvetica", "Arial", sans-serif !important; /* font-size: ~16px, line-height: ~24px; */
}
h1, h4 { margin-bottom:0; }
h1 {
font-size: 1.5rem; /* 24px / 16px */
font-weight: normal;
}
h4 {
font-size: 1.125rem; /* 20px / 16px */
font-weight: bold;
line-height: 1.5rem;
}
p {
font-size: 1rem; /* 16px / 16px */
line-height: 1.5rem!important;
margin-top: .5em;
}
.hljs { background:#F9FAFA!important; }
a { text-decoration: none; }
br.clear { clear:both; }
span.code-highlight1 {
font-style: italic;
color: green;
}
a.button:link, a.button:visited {
display: block;
width: 16em;
padding: 0.2em;
line-height: 1.4;
background-color: #94B8E9;
border: 1px solid black;
color: #000;
text-decoration: none;
text-align: center;
}
a.button:hover {
background-color: #369;
color: #fff;
}
table {
margin-left:2px;
margin-right:2px;
border-collapse: collapse;
font-family: "Century Gothic", "Gill Sans", Arial, sans-serif;
}
caption {
text-align: center;
}
td,th {
border: 1px solid #73afb7;
padding: 3px 5px 2px 5px;
}
th {
background:#93C2C8 repeat;
color: white;
text-align: left;
border-color: #14556b;
}
tr.alt td {
background:#F6F8EE repeat;
}
/* ///////////////////////////////////////////////// */
/* End custom styles */
/* ///////////////////////////////////////////////// */
</style>
<link href='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.9.0/styles/vs.min.css' rel='stylesheet'/>
<script src='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.9.0/highlight.min.js'></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.9.0/languages/swift.min.js'></script>
<script>hljs.initHighlightingOnLoad();</script>
<a href="https://c2.staticflickr.com/8/7341/13079348114_070f56008f_q.jpg" style="float: right;"><img alt="street outlaw" border="1" src="https://c2.staticflickr.com/8/7341/13079348114_070f56008f_q.jpg" /></a>
<p>"It’s time to upgrade". That’s a common theme you’ll hear if you watch <a href="http://www.discovery.com/tv-shows/street-outlaws/">Street Outlaws</a> on the Discovery Channel. Their goal is simple - be the fastest car on the street or it’s time to upgrade. They are continuously looking for ways to boost the performance of their cars (nitrous, turbos, fiberglass panels, etc). Some performance mods are quick and others take time to implement and refine. In this post, let's focus on Swift performance improvements we can implement and release rather quickly. In fact, most of these improvements should only cost a story point or two:</p>
<h4>Mark classes <em>final</em></h4>
<p>A final class will explicitly enable static dispatch for all methods which allows the compiler to optimize methods at compile time resulting in more efficient method invocation at runtime. There are two types of method dispatch: static and dynamic. Dynamic dispatch (the default type of method dispatch) is not as efficient because its dispatch isn’t known until runtime. Dynamic dispatch provides runtime conveniences like polymorphism, inheritance, and overloading but incurs a higher performance penalty. Therefore, by simply marking classes final we get a minor performance boost rather quickly. Refer to Apple’s <a href="https://developer.apple.com/videos/play/wwdc2016/416/">Understanding Swift Performance</a> talk for additional details.</p>
<h4>Prefer <em>isEmpty</em> over <em>count > 0</em></h4>
<p>The native <em>isEmpty</em> method is more efficient because it only verifies one element exists where as the <em>count</em> property iterates the entire collection. Want to enforce this efficient practice within your codebase? <a href="https://realm.io/news/slug-jp-simard-swiftlint/">SwiftLint</a> has a static analysis check for this rule.</p>
<h4>Convert strings to enums</h4>
<p>In Apple’s <a href="https://developer.apple.com/videos/play/wwdc2016/416/">Understanding Swift Performance</a> talk they mentioned three dimensions of performance (see Listing 1).</p>
<figure>
<figcaption>Listing 1. Dimensions of Performance</figcaption>
<table style="border-collapse: collapse;">
<tbody>
<tr><th style="text-align: center;background: #93C2C8 repeat; color:white; border-color:#14556b; border: 1px solid #73afb7;">Faster</th><th style="text-align: center;background: #93C2C8 repeat; color:white; border-color:#14556b; border: 1px solid #73afb7;">Dimension</th><th style="text-align: center;background: #93C2C8 repeat; color:white; border-color:#14556b; border: 1px solid #73afb7;">Slower</th></tr>
<tr><td align="center" style="border: 1px solid #73afb7;">Stack</td><td style="font-weight: bold; text-align: center; border: 1px solid #73afb7;">Allocation</td><td align="center" style="border: 1px solid #73afb7;">Heap</td></tr>
<tr><td align="center" style="border: 1px solid #73afb7;">Less</td><td style="font-weight: bold; text-align: center; border: 1px solid #73afb7;">Reference Counting</td><td align="center" style="border: 1px solid #73afb7;">More</td></tr>
<tr><td align="center" style="border: 1px solid #73afb7;">Static</td><td style="font-weight: bold; text-align: center; border: 1px solid #73afb7;">Method Dispatch</td><td align="center" style="border: 1px solid #73afb7;">Dynamic</td></tr>
</tbody></table>
</figure>
<p>When creating Swift objects it’s important to understand how much an object or property weighs in regards to these three dimensions. For example, a <em>String</em> is not an efficient type because it is allocated on the heap and it incurs a reference count. Comparatively, <em>Enums</em> are much more efficient because they are allocated on the stack, do not incur a reference count, and as a bonus they are type safe resulting in a win-win-win. Interested in automatically converting your localizable strings, asset names, or storyboard names to enums? <a href="https://github.com/AliSoftware/SwiftGen">SwiftGen</a> has accomplished this task nicely on my projects.</p>
<h4>Import existing images from the file navigator into an asset catalog</h4>
<p>An asset catalog is preferred because it caches images, optimizes memory consumption, and enables lightweight App Store downloads via App Thinning - unnecessary image densities are not downloaded. Xcode has a wizard for automatically importing images into an asset catalog rather quickly. Refer to Apple’s <a href="https://developer.apple.com/videos/play/wwdc2016/213/">Improving Existing Apps with Modern Best Practices</a> talk for additional details.</p>
<h4>Parse JSON on a background thread</h4>
<p>This allows the main thread to remain idle to serve other threads while the JSON is transformed to an object. Refer to Apple’s <a href="https://developer.apple.com/videos/play/wwdc2016/720/">Concurrent Programming with GCD in Swift 3</a> talk for more details.</p>
<pre style="white-space: pre-wrap; font-family:arial; font-size:12px; width:99%; height:auto; -webkit-overflow-scrolling:touch; overflow:auto; padding:0px; color:#000000; text-align:left; line-height:20px; border: 1px solid #d6d6d6; background-color:#F9FAFA!important;"><code style="color:#000000; background:#F9FAFA!important;" class="swift">let json = [String : Any]()
let dataTrasformQueue = DispatchQueue(label: “my.data.trasform.queue”)
dataTrasformQueue.async {
let result = T(json: json) // Transform JSON to object on background thread
DispatchQueue.main.async {
completionHandler(result) // Return control to main thread after object is parsed
}
}
</code></pre>
<h4>Prefer <em>structs</em> over <em>classes</em> (until you reach their point of diminishing returns)</h4>
<p>Structs have many advantages over classes: they are allocated on the stack, they prevent unintended sharing, their memberwise initializer is a nice convenience, and a struct by itself doesn't incur reference counting. However, a structs properties will incur reference counting when they are reference types like String. Therefore, structs have a point of diminishing returns in regards to reference counting that multiplies proportionally by their number of properties which are reference types. A struct containing more than 2 reference types begins to incur a higher reference count than a comparative class. Refer to Apple’s <a href="https://developer.apple.com/videos/play/wwdc2016/416/">Understanding Swift Performance</a> talk for additional details.</p>
<h4>Rewrite Objective-C objects in Swift</h4>
<p>Refactoring Objective-C objects to Swift was the primary theme in Apple’s <a href="https://developer.apple.com/videos/play/wwdc2016/406/">Optimizing App Startup Time</a> talk. This tip barely makes the list for “busy developers” because the scope of this task may be large depending on your codebase but it’ll become more manageable by focusing on converting one Objective-C class per week or iteration.</p>
<h4>Summary</h4>
<p>The fastest car doesn't always win. The quickest car from start to finish does. A driver and the adjustments they make to their car has a huge impact on their performance. The tips above are quick adjustments we can make to our Swift code so our apps will be more efficient from point A to point B.</p>Brad Broulikhttp://www.blogger.com/profile/14248585105589786365noreply@blogger.com0tag:blogger.com,1999:blog-7224548894465589501.post-28424133503425017732016-05-09T20:19:00.000-05:002017-02-08T19:53:47.970-06:00App Linking<p>Want to learn how to increase app engagement with universal links? In this tutorial we will review the end-to-end steps to get your Web server and app setup for <a href="https://developer.apple.com/library/ios/documentation/General/Conceptual/AppSearch/UniversalLinks.html">app linking</a>. From a user perspective app linking is as simple as tapping a link (see figure 1). Universal links will be routed to the app when the link has been enabled for app linking and the app is installed. However, if the app is not installed, the link will naturally open in the default browser.</p>
<figure>
<img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjdppfmFwhjhUKP5hn0LNNm5TWJk5DiMitkaS7zKWB09WDqWE1BvDV41hXB1xAOrMsWjeiFTewwBWUD0mgce2eFhD01Z6uaqCMFN6d9qLCC4ipnTGi9BL-P6Tu_9xd4cu4yxrq4JcalVZOo/s1600/app-linking.jpg" />
<figcaption>Figure 1. App linking workflow</figcaption>
</figure>
<br />
<h2>Get your server ready</h2>
<p>
The first task for enabling app linking is to configure the links or paths that should be associated to your app. This configuration is setup with the <code>apple-app-site-association</code> file that is deployed to the root of your HTTPS Web server.
</p>
<h4>Create the Apple association file</h4>
<p>Create the <code>apple-app-site-association</code> JSON file with <a href="https://developer.apple.com/library/ios/documentation/General/Conceptual/AppSearch/UniversalLinks.html#//apple_ref/doc/uid/TP40016308-CH12-SW3">Apple's recommended format</a>. This file identifies the paths or links that are linkable for your app. You may setup your path mappings to be <a href="https://developer.apple.com/library/ios/documentation/General/Conceptual/AppSearch/UniversalLinks.html#//apple_ref/doc/uid/TP40016308-CH12-SW3">specific or wildcard matchers</a>. For example, the example below (see Listing 1) will route all links to your app via a wildcard path matcher. The <code>appID</code> key is the team ID or app ID prefix, followed by the bundle ID.</p>
<figure>
<figcaption>Listing 1. Creating an <code>apple-app-site-association</code> file</figcaption>
<pre class="language-json line-numbers" style="display: block;
border: 1px solid #d6d6d6;
background: #F9FAFA;
margin-bottom: 1.5em;
padding: 20px 30px 20px;
white-space: pre;
word-wrap: normal;
overflow-x: auto;
-webkit-overflow-scrolling: touch !important;
transform: translate3d(0px, 0px, 0px);
overflow-wrap: normal;
display: block;
box-sizing: inherit;">
<code style="-webkit-font-smoothing: auto;
white-space: inherit;
box-sizing: inherit;">{
"applinks": {
"apps": [],
"details": [
{
"appID": "{team-id-OR-app-prefix}.{app-bundle-identifier}",
"paths": [ "*" ]
}
]
}
}
</code></pre>
</figure>
<p>Upload the <code>apple-app-site-association</code> to the root of your Apache server or in the <em>.well-known</em> subdirectory. The default Apache server root exists at /Library/WebServer/Documents.</p>
<h4>Configure the association file</h4>
<p>The MIME type for the <code>apple-app-site-association</code> file must be <code>application/json</code>. This can be configured in Apache's <em>httpd.conf</em> file (see Listing 2).</p>
<figure>
<figcaption>Listing 2. Configure MIME type for <code>apple-app-site-association</code> file</figcaption>
<pre class="language-html line-numbers" style="display: block;
border: 1px solid #d6d6d6;
background: #F9FAFA;
margin-bottom: 1.5em;
padding: 20px 30px 20px;
white-space: pre;
word-wrap: normal;
overflow-x: auto;
-webkit-overflow-scrolling: touch !important;
transform: translate3d(0px, 0px, 0px);
overflow-wrap: normal;
display: block;
box-sizing: inherit;">
<code style="-webkit-font-smoothing: auto;
white-space: inherit;
box-sizing: inherit;"><Directory "/Library/WebServer/Documents">
....
#
# Setup MIME type for Apple association file
#
<files apple-app-site-association>
Header set Content-type "application/json"
</files>
</directory>
</code></pre>
</figure>
<h4>Test the association file</h4>
<p>Start your local Apache Web server via the Terminal:</p> <pre class="language-bash"><code>$ sudo apachectl restart</code></pre> <p>Verify it is running at <em>http://localhost</em>.
Next, setup your local Web server for SSL. Apple requires the association file to be deployed to an HTTPS Web server. The simplest way to deploy your localhost as a publicly accessible Web server via HTTPS is with <a href="https://ngrok.com/">ngrok</a>. After downloading ngrok run the following command via Terminal (see Listing 3).</p>
<figure>
<figcaption>Listing 3. Starting ngrok</figcaption>
<pre class="language-bash line-numbers" style="display: block;
border: 1px solid #d6d6d6;
background: #F9FAFA;
margin-bottom: 1.5em;
padding: 20px 30px 20px;
white-space: pre;
word-wrap: normal;
overflow-x: auto;
-webkit-overflow-scrolling: touch !important;
transform: translate3d(0px, 0px, 0px);
overflow-wrap: normal;
display: block;
box-sizing: inherit;">
<code style="-webkit-font-smoothing: auto;
white-space: inherit;
box-sizing: inherit;">$ ./ngrok http 80
Tunnel Status online
Version 2.0.25/2.0.25
Region United States (us)
Web Interface http://127.0.0.1:4040
Forwarding http://b5bdc858.ngrok.io -> localhost:80
Forwarding https://b5bdc858.ngrok.io -> localhost:80
</code></pre>
</figure>
<p>The prior command to start ngrok (see Listing 3) assumes ngrok is installed at the root directory and localhost is running at port 80. Copy the HTTPS generated URL from ngrok (See Listing 3, line 8) and test it from a browser. Congrats, your localhost is now publicly accessible over HTTPS! This is also the HTTPS URL our iOS app will be associated with when testing locally.</p>
<p>Next, verify the MIME type for the association file is correctly configured with a content-type of <code>application/json</code> in Terminal (see Listing 4).</p>
<figure>
<figcaption>Listing 4. Verify MIME type</figcaption>
<pre class="language-bash line-numbers" style="display: block;
border: 1px solid #d6d6d6;
background: #F9FAFA;
margin-bottom: 1.5em;
padding: 20px 30px 20px;
white-space: pre;
word-wrap: normal;
overflow-x: auto;
-webkit-overflow-scrolling: touch !important;
transform: translate3d(0px, 0px, 0px);
overflow-wrap: normal;
display: block;
box-sizing: inherit;">
<code style="-webkit-font-smoothing: auto;
white-space: inherit;
box-sizing: inherit;">$ curl -I "https://b5bdc858.ngrok.io/apple-app-site-association"
HTTP/1.1 200 OK
Date: Sat, 07 May 2016 12:50:44 GMT
Server: Apache/2.4.18 (Unix) PHP/5.5.31
Last-Modified: Mon, 04 Apr 2016 12:06:48 GMT
ETag: "c9-52fa79025e600"
Accept-Ranges: bytes
Content-Length: 201
Content-type: application/json
</code></pre>
</figure>
<p>Our last test is to verify our domain with Branch's <a href="https://branch.io/resources/universal-links/">Universal Links Validator</a>. Copy your HTTPS URL and run it against their validator (see Figure 2).</p>
<figure>
<img border="0" height="300" width="600" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEidtF307KUhDaNw0JOriPnvhrU3iX9e9kt00XwflWg_xURvO4HjwacVRbznu3a-96L2zxqcRH4mYIDdcV2jJtM1RzRQsuYZZSm5ZeS-dctgm-4vZHb2B0YO53iMUQ44Za9LzfcL48D9yrWq/s1600/branch-validator-orig+8.18.33+AM.jpg" />
<figcaption>Figure 2. Universal link validator</figcaption>
</figure>
<br />
<h2>Get your app ready</h2>
<p>The final task is to enable our app for universal links and setup the code to intercept and manage link validation and routing.</p>
<h4>Enable Associated Domains in Xcode</h4>
<p>In Xcode, navigate to your target's Capabilities tab and toggle Associated Domains on. Then, add each domain which hosts the corresponding association file and prefix it with <em>applinks:</em> (see Figure 3).</p>
<figure>
<img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhvB7354QQI7GSmxTb4xTDPexgvAmDqCF_ubf7j4ZA6MawSDHWfjPpG14udDGbnRFIw1cX8SvcrtvI1Tj0PlgNL_03RDinUe04zePObrN4-SDWj9NqqocXQNgCCK9Z41vkuL9wKWoNw3sbR/s1600/xcode-associated-domains.jpg" />
<figcaption>Figure 3. Enable associated domains</figcaption>
</figure>
<h4>Update App Delegate</h4>
<p>Add a new <code>AppDelegate+AppLinking.swift</code> file with the following code to handle universal link validation and routing. This code simply prints the URL of the universal link and by default forwards to the initial view controller. Refer to Apple's WWDC video on <a href="https://developer.apple.com/videos/play/wwdc2015/509/">App Linking</a> for validation and routing best practices.</p>
<pre style="white-space: pre-wrap; border:1px solid #d6d6d6; background:#F9FAFA; padding:1em; overflow-x:scroll; -webkit-overflow-scrolling:touch;"><code>import UIKit
extension AppDelegate {
func application(application: UIApplication, continueUserActivity userActivity: NSUserActivity, restorationHandler: ([AnyObject]?) -> Void) -> Bool {
if let url = userActivity.webpageURL where userActivity.activityType == NSUserActivityTypeBrowsingWeb {
print("url=\(url)")
}
return true
}
}
</code></pre>
<br />
<h2>Testing App Linking</h2>
<p>After installing the app on a device, paste the HTTPS URL for your website as a link in any iOS app (Mail, Messages, Contacts, etc.) and click the URL (see Figure 1). Did the link take you to your app? If yes, congratulations! You have successfully setup app linking. If you encounter any issues refer to my tips below. After a successful test remove the installed app and re-test the link. Without the app installed the link behaves like an ordinary link and forwards to the website (see Figure 1).</p>
<br />
<h2>Tips</h2>
<ul>
<li><p>The <code>apple-app-site-association</code> file must not have a .json extension.</p></li>
<li><p>App link testing will only work from an actual device. Testing from a simulator will not work.</p></li>
<li><p>The instructions provided above are for iOS 9 and above. If you need to support iOS 8 you are required to sign the <code>apple-app-site-association</code> file. For additional information on iOS 8 support refer to Apple's <a href="https://developer.apple.com/library/ios/documentation/General/Conceptual/AppSearch/UniversalLinks.html#//apple_ref/doc/uid/TP40016308-CH12-SW4">note</a> on iOS 8 support. Fortunately, certificate signing is not required on iOS 9 and above when the association file is deployed to an HTTPS server.</p></li>
<li><p>App linking only works when clicking a link from within an Apple app (Mail, Messages, Contacts, etc). App linking will fail from third party apps like Gmail, Facebook, etc.</p></li>
<li><p>For additional information on App Linking refer to Apple's <a href="https://developer.apple.com/library/ios/documentation/General/Conceptual/AppSearch/UniversalLinks.html">documentation</a> or their WWDC talk on <a href="https://developer.apple.com/videos/play/wwdc2015/509/">App Linking</a>.</p></li>
</ul>Brad Broulikhttp://www.blogger.com/profile/14248585105589786365noreply@blogger.com0tag:blogger.com,1999:blog-7224548894465589501.post-42172015011722554182016-04-17T08:28:00.001-05:002016-04-17T08:28:45.186-05:00Freaky Fast Delivery - Responsive Performance Strategies<p>Want to learn how to build freaky fast sites that load within the mythical one-second barrier? In this <a href="https://speakerdeck.com/bbroulik/freaky-fast-delivery-responsive-performance-strategies">presentation</a>, we’ll explore the latest techniques we can apply to create responsible sites the achieve this goal. Topic discussed will include:</p>
<ul>
<li>Traditional vs enhanced page loading techniques</li>
<li>Loading JavaScript, CSS, fonts and content asynchronously</li>
<li>Compression techniques for requests, images, and assets</li>
<li>Caching techniques via Browser cache and Web storage</li>
<li>Performance budgets, anti-patterns, and monitoring tools</li>
</ul>
<h3>Source Code and Demos</h3>
<p>Click the "Demo" icon on each slide to run the performance technique via my GitHub Pages account or browse the source code - Enjoy!</p><br/>
<h3>Slide Deck Sneak Peek</h3>
<figure style="float:left;">
<img border="0" width="200px" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgXcb-jI59zsUVPhfrtRVeFkv5Kwk6gnMr-A_yyWQHwp1QKN2me7hZMHCOj4AAF9He8k3ZqnJ0gdvLHgt3-n-rAJXO-XXOWg4juKbKgmQNwFHFuyESQSCWQ4ef_TV_ucB484AeqB1Yug1YU/s320/2-stress.jpg" alt="Poor Performance Causes Stress" />
<figcaption style="text-align: center;"><em>Poor Performance Causes Stress</em></figcaption>
</figure>
<figure style="float:left;">
<img border="0" width="200px" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgg-e7l3O7MRpKulr3eKiaI48kwibOfe-hcl0yoB6wLMevos4ISguKze3pqptrGA0lNPxHE43yqJqkpTk7NFQtjbQFlIjTDPPhiKI9EeA6MHgGzFiReSAdO8UehLIYWOktho4rq01LO73Ki/s320/5-responsible-performance.jpg" alt="Responsible Performance" />
<figcaption style="text-align: center;"><em>Responsible Performance</em></figcaption>
</figure>
<p style="clear: both;">
<figure style="float:left;">
<img border="0" width="200px" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhxrEAJT2o0Ws6gj_X9oVxnXWRL9Ff7Of9U2_OsMLBX9eu9GvUuhhksPnkLB4pwFiH3MJvGgYpCiTHsYAtwxw1nDyc4Mrka5uHGZ69xEm_AILyZpbO2wvz5sdgtR2sjxhdfC77v8fgk0EBu/s320/10-responsive-images.jpg" alt="Responsive Images" />
<figcaption style="text-align: center;"><em>Responsive Images</em></figcaption>
</figure>
<figure style="float:left;">
<img border="0" width="200px" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh4m8LOqt9KM9eahfynIacPL30QpiYF6ijYVWnvR_roDKHox6SnRtCiDEZhmmbTU2S9nsVUj4pNcMMJTRewUio4z1eaVxGnyvEW73RYQA_6zD2QOyjDq_LzBnGzeOSpSzIMMryov9eMxzSp/s320/20-http2.jpg" alt="HTTP2"/>
<figcaption style="text-align: center;"><em>HTTP/2</em></figcaption>
</figure>
Brad Broulikhttp://www.blogger.com/profile/14248585105589786365noreply@blogger.com0tag:blogger.com,1999:blog-7224548894465589501.post-7508522305257347852015-12-08T21:00:00.000-06:002015-12-08T21:00:14.056-06:00Apple TV Developer Kit<p>Interested in developing for the Apple TV? Surprisingly, JavaScript (TVJS) and Markup (TVML) are the primary technologies for building Apple TV apps. In this <a href="https://speakerdeck.com/bbroulik/apple-tv-developer-kit">presentation</a>, we’ll explore the new Apple TV developer framework from setup to deployment and everything in between. Topics discussed will include:</p>
<ul>
<li>Project setup</li>
<li>Application structure</li>
<li>Running from the simulator or Apple TV</li>
<li>What apps are great candidates for the Apple TV</li>
</ul>
<h3>Source Code and Demos</h3>
<p>My <a href="https://github.com/BradBroulik/TVMLMovies">Apple TV demos</a> are available on Github. Enjoy!</p><br/>
<h3>Slide Deck Sneak Peek</h3>
<figure style="float:left;">
<img border="0" width="200px" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjubiAPYna1QJMOPPCPUZzf4qpqIFEyitzkg0zLnBGLWdTeV7-1zJi9TsKCX5WEHi6cnNtBqxHI1-MgF_t92oIbmgacgQO9AAmcqQUl9IjuwYfnAsZjEo9fT6ETY5tDJW88D-oSmlSxOMfC/s1600/3-tvml-app-architecture.jpg" />
<figcaption style="text-align: center;"><em>TVMLKit App Architecture</em></figcaption>
</figure>
<figure style="float:left;">
<img border="0" width="200px" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgcE9eN6p8vBiUwzdF8TcmhUiR-ERxldqGytL00qjkEeGgEQE8ZNRQqnSOpr3kL2KmgBOddr9rGCLZZOUtP7Ck4qVN0tNXJZtFKg2mhbsIb2scLULB49RmLCXEhllbbwQIUVu4LVU_OrCmI/s1600/5-tvmlkit-tech-stack.jpg" />
<figcaption style="text-align: center;"><em>TVMLKit Technology Stack</em></figcaption>
</figure>
<p style="clear: both;">
<figure style="float:left;">
<img border="0" width="200px" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh_QywkY_mHEzV9WiGorK6Hi6UtzIFiY3n5x4k5sbZZ0QTDslCdpOjLMvCu8T4DMuqY9PPh0AwQZXyq_8vzW7lTjeD3TENX44Dp83OSM4rg1kPjAwVmTa0wLGXFAhWjNsRsH_oO3MLQDIS6/s1600/8-swift.jpg" />
<figcaption style="text-align: center;"><em>Swift</em></figcaption>
</figure>
<figure style="float:left;">
<img border="0" width="200px" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhqnluCzqHm5ZE1bOJPNRRpxpBTo1bq97OrL6juOw2HWknwGfHoZ5eknVRqlI8gpBa_1TwBG5vta-BJpVfbhBY8fL3jWmQrg554nTUPBm3Gkna1Xu_rZp0yZgfU3MHLZpswmc67S3NWGro0/s1600/9-tvjs.jpg" />
<figcaption style="text-align: center;"><em>TVJS</em></figcaption>
</figure>
<p style="clear: both;">
<figure style="float:left;">
<img border="0" width="200px" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh2tm_UeFeTElKh_ctEOAKg_hG1DcjeKbdiFh0mr-kMIwNsYJFE0mO0swVjJoI7u8jLedqTSwfH30c3uk-k7Vdx5e0k0YEfWzUloDvGwR6_WEfRfoVBmdy2tYkOX4xTSOYA-DgcWbmSKzC4/s1600/10-tvml.jpg" />
<figcaption style="text-align: center;"><em>TVML</em></figcaption>
</figure>
<figure style="float:left;">
<img border="0" width="200px" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEih_7bAEqq3rVOrzc7fhvK_zq_crtUeh6jVuYLLK0l_6l616LS8yNylQHNoJPfOd6Goswljwvt7uvUGx9-cvZvhG46MymnAoBzeeDMItwXNNvkFJFzOebVtSpWWgyxweaVwZjXFL65NUFRw/s1600/11-catalog-template.jpg" />
<figcaption style="text-align: center;"><em>Catalog TVML Template</em></figcaption>
</figure>Brad Broulikhttp://www.blogger.com/profile/14248585105589786365noreply@blogger.com0tag:blogger.com,1999:blog-7224548894465589501.post-43380760706216508722015-12-08T20:32:00.001-06:002015-12-08T20:32:44.535-06:00UI Testing iOS & Android Apps<p>Want to learn how to <a href="https://speakerdeck.com/bbroulik/ui-testing-ios-and-android-apps">UI test iOS and Android apps</a>? In this <a href="https://speakerdeck.com/bbroulik/ui-testing-ios-and-android-apps">presentation</a>, we’ll explore the latest iOS and Android UI testing techniques to create automated UI tests. Topic discussed will include:</p>
<ul>
<li>UI Testing advantages.</li>
<li>For each platform we will learn how to setup, create, and run our UI tests. We will also learn how to setup continuous integration environments.</li>
<li>iOS topics will include:</li>
<ul>
<li>We will explore Xcode 7’s new UI testing framework. It is very intuitive and greatly improves test setup with automated UI recording capabilities.</li>
<li>Review the new iOS test framework API.</li>
<li>Setup a continuous integration environment in Xcode Server.</li>
</ul>
<li>Android topics will include:</li>
<ul>
<li>Creating UI tests with Android’s Espresso 2.2 framework.</li>
<li>Review Espresso's test framework API.</li>
<li>Setup a continuous integration environment in the cloud with CircleCI.</li>
</ul>
<li>UI Testing best practices</li>
</ul>
<h3>Source Code and Demos</h3>
<p>My <a href="https://github.com/BradBroulik/iOS-adaptive-demos">iOS UI tests</a> and <a href="https://github.com/BradBroulik/android-adaptive-demos">Android UI tests</a> are both available on Github. Enjoy!</p><br/>
<h3>Slide Deck Sneak Peek</h3>
<figure style="float:left;">
<img border="0" width="200px" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg9uiOBAukNXByQXzIOqNQtiL-2pdVTibEIa4ngHMfDH5Dyoeg1oPMCP_YblWWm44Zrf-X2CdOp0fa-kT_9YuB4TxpJe9UKhUI1Da_L7Ak10UQ_L2ky1Ay44UJ3eIsTSqjOWdCNwpEZkl6t/s1600/2-ui-testing-advantages.jpg" />
<figcaption style="text-align: center;"><em>UI Testing Advantages</em></figcaption>
</figure>
<figure style="float:left;">
<img border="0" width="200px" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiTWQe5gCaxrT-z6nR-I9pOw5l5u7kU_Bj92_FXWh3LBENZDCDJySiaen9rO7bbHVIGRW3DTojKmahbBolkX4a5ZWl75Mk-NiyDfJBCaLODTre981lCBiSxq96lCdYeTHoKdSUloPfZ_bt1/s1600/3-test-pyramid.jpg" />
<figcaption style="text-align: center;"><em>Test Pyramid</em></figcaption>
</figure>
<p style="clear: both;">
<figure style="float:left;">
<img border="0" width="200px" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgEZ5C8iTZzk2WszhpabvDrWJLTyGBKrJhRDUn2bFZ-L334dDsPWbaRHC4KaN2znZAGohVDfPmUaRTKIlAUAjcvD22X9AL-ETVJmuXuHlq5byvDe9T2t_aSLxDmu3wV8QNOxTq07LBF0tK3/s1600/18-xcode-server-ci.jpg" />
<figcaption style="text-align: center;"><em>iOS CI with Xcode Server</em></figcaption>
</figure>
<figure style="float:left;">
<img border="0" width="200px" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjLxhrGgPZI9LM8k_PlLnD5c1gBgMOgfy0g8SnahRL9kyTgvBrkNtIMD3OFMN3knPyM3UdPoJyO1EbVdsjduncvF1jyB11F-rHGKNCZAKxkWWhrKB9NUljGuPvH-Y4Bw2vKm6nlnbH8NsIP/s1600/19-xcode-report-navigator.jpg" />
<figcaption style="text-align: center;"><em>Xcode’s Report Navigator</em></figcaption>
</figure>
<p style="clear: both;">
<figure style="float:left;">
<img border="0" width="200px" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjnhoufUbePbhxFvL5bNcdNwVURFoqx9il-Hsnb8xGgZYEhoVet9enHsuBLIZwFaFxY_Gm6kECslsiuHOkVBJJkL6iYtCEanMhafbjRkOMznTIU9EZCKSVKqZ50nJtg0aKjxPeEmzkn-kEL/s1600/28-android-matchers.jpg" />
<figcaption style="text-align: center;"><em>Android’s ViewMatcher</em></figcaption>
</figure>
<figure style="float:left;">
<img border="0" width="200px" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjcQpBnk9nlfXbxIr0fbFGvin4CirIDmmatwv-GQwy3CtnaK_9jvHDhNUHjOZhgI0CEE-BdLoSiCJB1BcwmJK7atwoG3CX9Ks500AQ6tG7yDzUCp91t2rG0pWQ-l122dND2_rzs_EiWjZGc/s1600/34-circleci.jpg" />
<figcaption style="text-align: center;"><em>Android CI with CircleCI</em></figcaption>
</figure>Brad Broulikhttp://www.blogger.com/profile/14248585105589786365noreply@blogger.com0tag:blogger.com,1999:blog-7224548894465589501.post-82152020902570741902015-04-27T21:26:00.001-05:002015-04-29T17:49:13.930-05:00Building Adaptive iOS and Android Apps<p>Want to learn how to create <a href="https://speakerdeck.com/bbroulik/building-adaptive-ios-and-android-apps">Adaptive iOS and Android Apps</a> that are optimized for both small and large devices? In this <a href="https://speakerdeck.com/bbroulik/building-adaptive-ios-and-android-apps">presentation</a>, I explore the latest iOS and Android techniques we can apply to create universal apps that adapt their content for all device sizes. Topic discussed include:</p>
<ul>
<li>Adaptive advantages</li>
<li>Adaptive layouts - master/detail, grids, and custom layouts</li>
<li>Adaptive images - images by size class and vector icons</li>
<li>Adaptive content - popovers, self-sizing cells, and dynamic type</li>
<li>Adaptive testing - resizable emulators and previewing multiple layouts</li>
</ul>
<h3>Source Code and Demos</h3>
<p>My <a href="https://github.com/BradBroulik/iOS-adaptive-demos">adaptive iOS demos</a> and <a href="https://github.com/BradBroulik/android-adaptive-demos">adaptive Android demos</a> are both available on Github. Enjoy!</p><br/>
<h3>Slide Deck Sneak Peek</h3>
<figure style="float:left;">
<img border="0" width="200px" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg4nIfVfOjJgu-nzXOEQ5yPe8SmRCb-AcE-x9YkbDSQGW_2VQN0U6VyhzJyoRQR4VFFRjY0qpMBDlowqJq7khT8cUAb_FGQ52lqLmpd7SPrz4wVbHnl8hxDv53BF2LkAkZb5LIhWUyGCQLK/s1600/5.png" />
<figcaption style="text-align: center;"><em>Adaptive Advantages</em></figcaption>
</figure>
<figure style="float:left;">
<img border="0" width="200px" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj9muRbeyGDB0B6zp78JOjSNXbPx6Is83HCFaqsWllcb_O1O4qZUyTrhdWMBuNppC2dk5AuvwTCYy0C8Qsmeg_RxRXbTKKR3VETYe-2aoSzb3UH8-I27UWwEeY5BQsNbTe0Ixjx0xvxAn92/s1600/13.png" />
<figcaption style="text-align: center;"><em>iOS SplitViewController</em></figcaption>
</figure>
<p style="clear: both;">
<figure style="float:left;">
<img border="0" width="200px" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh0IPvNsFUQDPudVN9MjY4024AbhTNBHpMCkbiR9twimpLHSENBfkjnhidn0lIKlmPGub1n9gMqS0GdKP62ihldUFffmCUV7Nu6S4i1AVOXGgHnkH9guwdBMKS10z3fDKouHbLbwYRCdMZh/s1600/17.png" />
<figcaption style="text-align: center;"><em>Auto Layout and Constraints</em></figcaption>
</figure>
<figure style="float:left;">
<img border="0" width="200px" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi3M3tFVtm5RWgLDMvrrJHAr_dcsmvtRPP7HsHSjWKHhAEbsgCWoJ5K8IroeBEyQhw06o_VXf87-TVqQoUZ9aFhVCwpuXZ0kTcjqLXryPQFh62vuH1p-7Mwwx3Sx2i-pJpsFeH1FOD1pyEU/s1600/38.png" />
<figcaption style="text-align: center;"><em>GridView and CardView</em></figcaption>
</figure>
<p style="clear: both;">
<figure style="float:left;">
<img border="0" width="200px" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiBWobkdxyJG4xiAR3yBOrcMUIYS4lhybRh-GuhDe7d_ZZ10CdXes8NA-bY36qNbkyKl8qbZSIAYEzJsXQgLkw7Rp0WRcDFhqeDDZndGrixWFoRyXFRcFmMivSAQshTtocsTG3hsn92YXk5/s1600/23.png" />
<figcaption style="text-align: center;"><em>Popovers</em></figcaption>
</figure>
<figure style="float:left;">
<img border="0" width="200px" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgX-tMJJ1sYyP5IPlILkToVJPJM9fSr_cBVZIyETk9Ssg-WU4OQnsnbNrKCfixAZao5GLqYfIzndYcTRSy0Nu7jgG7gMmHp5D-Cm06povqvNmS0MTtJAKwK8rCqx2vFxSfnxqWnrrgHvMEB/s1600/29.png" />
<figcaption style="text-align: center;"><em>Resizable Simulator</em></figcaption>
</figure>
Brad Broulikhttp://www.blogger.com/profile/14248585105589786365noreply@blogger.com0tag:blogger.com,1999:blog-7224548894465589501.post-71864411230212640532014-01-10T12:50:00.000-06:002014-06-28T22:08:48.724-05:00jQuery Mobile Events DiagramWhen building jQuery Mobile applications I often find it useful to have a quick reference to all events within the lifecycle. This jQuery Mobile events diagram should be helpful for new developers learning the jQuery Mobile event model or advanced developers that often bind to these events:<br /><br /><br /><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhCYQT1hX2z5gh1RSnh-gJVjCBXRnz4NWjc7fItWMS7jpY23gp3XHFpE2Nw-bMq0vDgAMQy2_XtAyLzYyLfK8Zj2HxT0627NfiPmME5qkpowhe025BabxXCmi5aUjPsGXJd1XXJNZkRnbyL/s1600/jqm-events-1.4.001.jpg" imageanchor="1" ><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhCYQT1hX2z5gh1RSnh-gJVjCBXRnz4NWjc7fItWMS7jpY23gp3XHFpE2Nw-bMq0vDgAMQy2_XtAyLzYyLfK8Zj2HxT0627NfiPmME5qkpowhe025BabxXCmi5aUjPsGXJd1XXJNZkRnbyL/s1600/jqm-events-1.4.001.jpg" height="495" width="660"/></a><br /><br /><br />This diagram is also available within <a href="http://www.amazon.com/Pro-jQuery-Mobile-Brad-Broulik/dp/1430239662/ref=sr_1_9?s=books&ie=UTF8&qid=1320859255&sr=1-9" target="new">Pro jQuery Mobile</a>. In addition to the diagram, you will also find detailed examples and descriptions for each event. My events diagram for <a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjsfFPH12RORBV7Xty7Ighx9PXim8foSG4urbLB0LjYiH_YM3-41RdfueE48K2i_4-crYZZjcjeIGXdN5NeHH5bOCL0SOJNyBXvpbKfZ7ULfXrRduKIKpk9JnLCs7hesKR-_IOAYrFk7CgZ/s1600/fig1-jqm-page-eventsF.png" imageanchor="1" >jQuery Mobile 1.3 and earlier</a> is also available. Enjoy!
Brad Broulikhttp://www.blogger.com/profile/14248585105589786365noreply@blogger.com16tag:blogger.com,1999:blog-7224548894465589501.post-89995566885209548892012-02-19T12:20:00.002-06:002012-04-12T18:09:04.848-05:00jQuery Mobile: Performance Tips<a href="http://www.flickr.com/photos/bluestarink/6091770154/" style="float:right"><img src="http://farm7.staticflickr.com/6208/6091770154_4a6898bd3f_t.jpg" border="1" alt="shark"/></a>Do you know what swimmers wore in the last summer Olympics to gain a performance advantage? They wore <a href="http://www.mnn.com/earth-matters/wilderness-resources/photos/7-amazing-examples-of-biomimicry/sharkskin-swimsuit">sharkskin suits</a>. The swimmers that wore this new technology won more races and broke more world records than ever before. Unfortunately, mobile development requires much more effort than simply sliding on a suit to gain more performance. Mobile developers are challenged with multiple limitations that affect performance: a network with limited bandwidth and underpowered devices. As we build mobile applications, it is important to code review the entire project with an emphasis on performance. Listed below are several tips for improving the performance of your jQuery Mobile applications: <br /><br /><br /><h3>Prefer Native jQuery Mobile Widgets</h3> We can gain several implicit advantages by reusing jQuery Mobile's native widgets. They are compatible across all browsers, they simplify maintenance, and more importantly, they save us from writing additional custom code that adds overhead to our applications. While there will be occasions when we need to build custom widgets, it is important to consider all native solutions prior to choosing a custom alternative. I have seen several instances where custom alternatives were built when native solutions would have provided similar behavior with much less overhead. For instance, I was recently reviewing a jQuery Mobile app that had built a custom message box when the same effect could have been designed with a native inset list (see Figure 1-1).<figure><img src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEitxAo9BanUADnvgxCeYjfK96fZlgWsycb-ALZIXXVaKckHEQXlDzGJJxgdV0XmvSXSPjjJ62b6yXC2pnoyN_jLsqsPFmwGM66FiKgZNGTW-_S6ZjbzV18eZkXJMizcQQ5Q8xqZBUHqUS8t/s1600/custom-versus-native-sm.png" alt="Native versus Custom Message Box"><figcaption><strong>Figure 1-1.</strong> Native versus Custom Message Box</figcaption></figure><br /><br />The native solution is significantly leaner containing 80% less CSS versus the custom solution (compare Listing 1-1 versus 1-2).<br /><figure><figcaption><strong>Listing 1-1.</strong> Custom Message Box</figcaption><pre class="prettyprint"><style><br />.message-box {<br /> background-color: #ECE8D3;<br /> color: #816d49;<br /> font-size: 0.75em;<br /> line-height: 1.3;<br /> margin: 0 0 10px;<br /> float: left;<br /> width: 100%;<br /> -moz-box-shadow: 0 0px 10px rgba(0, 0, 0, 0.3);<br /> -webkit-box-shadow: 0 0px 10px rgba(0, 0, 0, 0.3);<br /> box-shadow: 0 0px 10px rgba(0, 0, 0, 0.3);<br /> -moz-border-radius: 0.6em;<br /> -webkit-border-radius: 0.6em;<br /> border-radius: 0.6em;<br />}<br />.message-box .inner {<br /> padding: 10px 12px;<br />}<br />.message-box p {<br /> color: #816d49;<br /> font-size: 11px;<br /> text-shadow: none;<br /> font-weight: normal;<br />}<br /></style><br /><br /><div data-role="content"><br /> <div class="message-box"><br /> <div class="inner"><br /> <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna.</p><br /> </div><br /> </div><br /></div><br /></pre><br /></figure><figure><figcaption><strong>Listing 1-2.</strong> Native Message Box</figcaption><pre class="prettyprint"><style><br />.ui-li-message {<br /> padding-top:10px;<br /> white-space: normal;<br />}<br /></style><br /><br /><div data-role="content"><br /> <ul data-role="listview" data-inset="true" data-theme="e"><br /> <li><br /> <p class="ui-li-message">Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna.</p><br /> </li><br /> </ul><br /></div><br /></pre></figure><br /><br />Another example I also encountered was the usage of <a href="http://dev.jtsage.com">JTSage's</a> SimpleDialog plugin. While I have found value with JTSage's DateBox plugin for configuring date ranges, I have not seen a compelling advantage to consider their SimpleDialog versus the native dialog we get out-of-the-box with jQuery Mobile. Again, the native dialog provides similar behavior with much less overhead.<br /><br /><br /><h3>Removed Unused Themes</h3> If you plan to style your entire jQuery Mobile application with custom themes it is preferred to use the structure-only CSS file from jQuery Mobile's <a href="http://jquerymobile.com/download">download</a> site. This is a lightweight alternative for applications that do not need the default themes and it simplifies the management of the custom themes (see Listing 1-3).<br /><figure><figcaption><strong>Listing 1-3.</strong> jQuery Mobile's structure file without default themes</figcaption><pre class="prettyprint"><head> <br /> <meta charset="utf-8"><br /> <title>Custom Theme</title> <br /> <meta name="viewport" content="width=device-width, initial-scale=1"><br /> <strong><link rel=stylesheet href="css/theme/custom-theme.css" /><br /> <link rel=stylesheet href="css/structure/jquery.mobile.structure.css"/></strong><br /> <script type="text/javascript" src="jquery-min.js"></script><br /> <script type="text/javascript" src="jquery.mobile-min.js"></script><br /></head><br /></pre></figure>Additionally, to reduce HTTP requests, it is preferred to merge both the custom theme and structure files before deploying to a production environment.<br /><br /><br /><h3>Prefetch Secondary Pages that are Accessed Frequently</h3> Most often, it is preferred to leverage the single-page model and dynamically append frequently accessed pages into the DOM in the background. We can achieve this behavior by adding the <em>data-prefetch</em> attribute to any link we want to dynamically preload:<pre class="prettyprint"><a href="prefetch.html" <strong>data-prefetch</strong>>Prefetched Link</a></pre>This hybrid approach allows us to selectively choose which secondary links we want to dynamically load and cache. This pattern is only recommended for pages that are accessed very frequently because this behavior will trigger an additional HTTP request.<br /><br /><br /><h3>Remove Unused Plugins</h3> The jQuery Mobile framework contains many valuable plugins. However, if your application does not use all features then you can make your app a bit leaner by simply <a href="http://jquerymobile.com/blog/2011/08/03/jquery-mobile-beta-2-released/#features">removing the unnecessary plugins</a>. For instance, I was able to save 6KB by simply removing the following plugins from the uncompressed jQuery Mobile library: collapsible, collapsibleset, checkboxradio, select, and slider. The jQuery Mobile team is currently creating a <a href="http://jquerymobile.com/blog/2011/09/29/jquery-mobile-1-0rc1-released/#downloadbuilder">Download Builder</a> that will help simplify this task in the future.<br /><br /><br /><h3>Cache Highly Accessed Read-Only Pages</h3> We can cache jQuery Mobile pages by attaching the <em>data-dom-cache="true"</em> attribute on the page container:<br /><pre class="prettyprint"><div data-role="page" <strong>data-dom-cache="true"</strong>></pre>The cached pages are persisted in the DOM. While this feature is advantageous for frequently-accessed, lightweight pages, its usage needs to be monitored carefully to assert the DOM remains at a manageable size.<br /><br /><br /><h3>Prefer the CDN-hosted minified and gzipped jQuery Mobile files</h3> The files from the jQuery Mobile's <a href="http://jquerymobile.com/download/">CDNs</a> are highly optimized and will provide a more responsive experience for your users. They are compressed, cached, minified, and can be loaded in parallel.<br /><br /><br /><h3>Mobile Web Performance Analysis Tools</h3>If you are interested in evaluating your own mobile sites, here are several popular analysis tools that offer something unique in regards to how they grade for performance:<br /><ul><li><a href="http://developer.yahoo.com/yslow/mobile/">YSlow for Mobile:</a> A Bookmarklet that grades internal and external sites from either a mobile or desktop browser.</li><li><a href="http://www.blaze.io/mobile/">Blaze:</a> Evaluates the performance of your site from a physical device that is running in the cloud. This tool only allows you to evaluate external sites.</li><li><a href="http://calendar.perfplanet.com/2010/mobile-performance-analysis-using-pcapperf/">pkapperf:</a> Captures performance metrics from your device's physical network traffic. This tool allows you to evaluate both internal and external sites.</li></ul><br /><br />Faster mobile networks and mobile CPU's will eventually become our shark skinned suits and mobile sites will become faster without any additional effort. While this is promising for developers, the Olympic swimmers are facing a new challenge. Those famous shark suits have been banned for the 2012 Summer Olympics.Brad Broulikhttp://www.blogger.com/profile/14248585105589786365noreply@blogger.com4tag:blogger.com,1999:blog-7224548894465589501.post-37072318995399967562011-05-21T13:26:00.002-05:002011-05-21T13:29:41.802-05:00Titanium MVC PatternAre you looking to simplify your Titanium programming model? Leveraging the MVC pattern will help produce cleaner code and promote reusability. Applying the MVC pattern within Titanium is relatively easy and it provides all the advantages we expect from MVC. Here is a quick example of the view and controller components:<br /><br /><br /><h3>View</h3>The <em>view</em> is responsible for creating the window and adding the necessary UI components. Your views may optionally manage styling properties (color, font, and positioning). Ideally, the better practice is to extract all styling properties into external JSS files but I found this feature to be too quirky in its initial release.<br /><br /><pre><span class="code-highlight1"><br />/**<br /> * View Template<br /> * Usage: <em>APP.ui.LoginView.createWindow();</em><br /> */</span><br />APP.ui.LoginView = (<span style="color: rgb(0, 51, 102); font-weight:bold">function</span>()<span style="color: rgb(0, 136, 0); font-weight: bold;">{</span><br /><br /> <span class="code-highlight1">/* private methods to build UI specific components */</span><br /> <span style="color: rgb(0, 51, 102); font-weight:bold">function</span> buildWindow()<span style="color: rgb(0, 136, 0); font-weight: bold;">{</span><br /> <span style="color: rgb(0, 51, 102); font-weight:bold">return</span> Ti.UI.createWindow(<span style="color: rgb(0, 136, 0); font-weight: bold;">{</span><br /> title: <span style="color: rgb(51, 102, 204);">'Title'</span>,<br /> barColor: <span style="color: rgb(51, 102, 204);">'#225377'</span>,<br /> backgroundColor: <span style="color: rgb(51, 102, 204);">'#d4d2d3'</span><br /> <span style="color: rgb(0, 136, 0); font-weight: bold;">}</span>);<br /> <span style="color: rgb(0, 136, 0); font-weight: bold;">}</span><br /><br /> <span style="color: rgb(0, 51, 102); font-weight:bold">function</span> buildUsernameTextField(win)<span style="color: rgb(0, 136, 0); font-weight: bold;">{</span><br /> win.usernameTextField = Ti.UI.createTextField(<span style="color: rgb(0, 136, 0); font-weight: bold;">{</span><br /> autocorrect: <span style="color: rgb(51, 102, 204);">false</span>,<br /> hintText: <span style="color: rgb(51, 102, 204);">'Username'</span>,<br /> left: <span style="color: rgb(51, 102, 204);">10</span>,<br /> width: <span style="color: rgb(51, 102, 204);">285</span>,<br /> suppressReturn:<span style="color: rgb(51, 102, 204);">false</span>,<br /> autocapitalization: Ti.UI.TEXT_AUTOCAPITALIZATION_NONE,<br /> clearButtonMode: Ti.UI.INPUT_BUTTONMODE_ONFOCUS,<br /> borderStyle: Ti.UI.INPUT_BORDERSTYLE_NONE,<br /> returnKeyType: Ti.UI.RETURNKEY_NEXT<br /> <span style="color: rgb(0, 136, 0); font-weight: bold;">}</span>);<br /> <span style="color: rgb(0, 136, 0); font-weight: bold;">}</span><br /><br /> <span class="code-highlight1">/* Public API only exposes the createWindow method. */</span><br /> <span style="color: rgb(0, 51, 102); font-weight:bold">return</span> <span style="color: rgb(0, 136, 0); font-weight: bold;">{</span><br /> createWindow: <span style="color: rgb(0, 51, 102); font-weight:bold">function</span>()<span style="color: rgb(0, 136, 0); font-weight: bold;">{</span><br /> <span style="color: rgb(0, 51, 102); font-weight:bold">var</span> win = buildWindow();<br /> buildUsernameTextField(win);<br /> <span class="code-highlight1">//buildComponentX(win);</span><br /> <span style="color: rgb(0, 51, 102); font-weight:bold">return</span> win;<br /> <span style="color: rgb(0, 136, 0); font-weight: bold;">}</span><br /> <span style="color: rgb(0, 136, 0); font-weight: bold;">}</span>;<br /><span style="color: rgb(0, 136, 0); font-weight: bold;">}</span>)();<br /></pre><br /><br /><h3>Controller</h3>The <em>controller</em> is responsible for managing events (button taps) and navigation. Events handlers communicate with server-side services when necessary.<br /><br /><pre><span class="code-highlight1">/**<br /> * Controller Template<br /> * Usage: <em>APP.ui.LoginController.init();</em><br /> */</span><br />APP.ui.LoginController = (function()<span style="color: rgb(0, 136, 0); font-weight: bold;">{</span><br /><br /> <span class="code-highlight1">/* Private helper methods */</span><br /> <span style="color: rgb(0, 51, 102); font-weight:bold">function</span> setEventListeners(win)<span style="color: rgb(0, 136, 0); font-weight: bold;">{</span><br /> win.usernameTextField.addEventListener('return', <span style="color: rgb(0, 51, 102); font-weight:bold">function</span>()<span style="color: rgb(0, 136, 0); font-weight: bold;">{</span><br /> win.passwordTextField.focus();<br /> <span style="color: rgb(0, 136, 0); font-weight: bold;">}</span>);<br /> <br /> win.passwordTextField.addEventListener('return', <span style="color: rgb(0, 51, 102); font-weight:bold">function</span>()<span style="color: rgb(0, 136, 0); font-weight: bold;">{</span><br /> handleLoginEvent(win);<br /> <span style="color: rgb(0, 136, 0); font-weight: bold;">}</span>);<br /> <span style="color: rgb(0, 136, 0); font-weight: bold;">}</span><br /><br /> <span class="code-highlight1">/* Public API only exposes init method. */</span> <br /> <span style="color: rgb(0, 51, 102); font-weight:bold">return</span> <span style="color: rgb(0, 136, 0); font-weight: bold;">{</span><br /> init: <span style="color: rgb(0, 51, 102); font-weight:bold">function</span>()<span style="color: rgb(0, 136, 0); font-weight: bold;">{</span><br /> <span style="color: rgb(0, 51, 102); font-weight:bold">var</span> win = APP.ui.LoginView.createWindow();<br /> setEventListeners(win);<br /> win.open();<br /> <span style="color: rgb(0, 136, 0); font-weight: bold;">}</span><br /> <span style="color: rgb(0, 136, 0); font-weight: bold;">}</span>;<br /><span style="color: rgb(0, 136, 0); font-weight: bold;">}</span>)();<br /></pre><br /><br /><h3>Model</h3>The model is typically a JSON payload retrieved from a Restful service. For example, the <em>handleLoginEvent</em> will return user profile information after a successful login. We can save this object locally and leverage it on subsequent screens.<br /><br /><br />Advantages:<ul><li>Small objects and methods.</li><li>Improved organization and readability</li><li>Promotes reusability of views. Also, the decoupled views allow you to swap in native-specific UIs when necessary all while reusing a single controller.</li><li>Simpler maintenance.</li><li>Allows for simpler unit testing with responsibilities split into separate objects.</li></ul><br /><br />I expect many developers may fall in the trap of modeling their JavaScript according to the <a href="http://developer.appcelerator.com/doc/kitchensink" target="new">KitchenSink</a> examples. While those examples are useful, their programming practices should not be followed for a production ready app. For example, their examples do not leverage closure to eliminate global scope, they do not use a namespace convention for improved organization, and all MVC responsibilities were bundled within a single object.Brad Broulikhttp://www.blogger.com/profile/14248585105589786365noreply@blogger.com9tag:blogger.com,1999:blog-7224548894465589501.post-15493138996357573942011-03-20T09:04:00.002-05:002012-02-17T17:22:37.282-06:00Mobile MarchThe <a href="http://mobilemarchtc.com/" target="new">Mobile March</a> conference was held at the Best Buy headquarters this weekend. Here is a brief recap of the sessions I attended:<br /><br /><img width="650" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhNCn6Ki5WpqkEqUkCw5qBvnjp12_O-Udha9GV_c_AGjiWVCdoAhs_qflu4P6f9frqMLX-rTF_TxCsDfIbn8oUUqZbIMF5PBc6YZcN5KBJk8VApcgTHgE9NAsTfVu5KNfwiAwlnxF9rY1GL/s1600/sessions-am.png" alt="Morning Sessions" /><br /><br /><h3>Keynote</h3>Key points:<ul><li>There is a low tolerance for mediocrity in mobile. Apps have a 50% abandon rate after the first 30 days.</li><li>Mobile trends: News via tablets, social media, location based apps, group messaging with apps like <a href="http://belugapods.com/" target="new">Beluga</a>.</li><li>Mobile marketing, what's working: Incentives, deals, hot buys, and rebates.</li><li>5 things to consider: Activate advertising with text or QR codes, activate sponsorships with mobile, launch mobile coupons, test a location based campaign, use mobile to drive app downloads.</li></ul><p><em>Simple and low cost mobile technologies have proven to be very effective solutions. SMS, QR codes, and social media fall within this category. The goal is simple, get customers to engage and then extend the conversation.</em></p><br /><br /><h3>Cracking the Code: QR Codes and Coupons</h3>Key points:<ul><li>QR code scanning is up 1000% in the last 6 months. Consumers love to scan QR barcodes.</li><li>QR codes link users to additional information. For example, Best Buy has QR codes next to each price tag. Launching the QR code provides the user with detailed information and user reviews. Additionally, if you scan another product you can see a side-by-side comparison of both products.</li><li>Looking for a QR reader? Try <a href="http://itunes.apple.com/us/app/neoreader/id284973754?mt=8" target="new">NeoReader</a>.</li><li><a href="http://blogs.citypages.com/food/2011/01/chino_latinos_s.php" target="new">Chino Latino</a> is the first restaurant to put a QR code on a billboard.</li></ul><p><em>Mobile March had QR codes at the entrance of each session. Scanning the code displayed the presenters bio. Again, this is a simple, low cost solution to interact with mobile customers.</em></p><br /><br /><h3>The iPhone vs Android Showdown</h3>Key points:<ul><li>iPhone</li><ul><li><a href="http://developer.apple.com/xcode/" target="new">Xcode 4</a>, which was released a few weeks ago, has finally simplified the organization of their Interface Builder and code editor. Navigating between Interface Builder and code is much quicker via their new tab orientation instead of externalized windows found in Xcode 3.</li><li>Xcode is the superior IDE when compared to Android development on Eclipse. The Interface Builder is far superior and the iPhone emulator loads much faster.</li></ul><li>Android</li><ul><li>Android's distribution model is superior. It takes several hours to deploy to Android's Market. The Apple Store certification process may take up to 2 weeks.</li></ul></ul><p><em>Creating a mobile wireframe is easily 2-3 times faster in Xcode. While Android finally released a UI builder with their 3.0 SDK it is far from perfect. A major Android advantage is their time to market on app upgrades. If you have a production defect will you have the patience to wait for the Apple certification process?</em></p><br /><br /><h3>Grill yourself</h3>Key points:<ul><li>Think about UX as soon as you have an idea.</li><li>Keep apps simple and moving parts to a minimum.</li><li>You can sell anything if you can do it simple, quick, and cheap.</li><li>Share the analytics about your mobile users with your organization.</li></ul><br /><br /><img width="650" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjMZ3Svp-yOIsTefjP2wHbOoOtEOMkkbAISKs3CPV1V7aQDLCuG3L08waxZy3nKehR9bKNwaCcQ8tMYuAaowTS_tlOYJmJXlGQv2Oj99L48uYvja5MUU1oCena3SwitTD3A3p2sDZzxx5ra/s1600/sessions-pm.png" alt="Afternoon Sessions" /><br /><br /><h3>The Current State of the Mobile Web</h3>Key points:<ul><li>Native advantages: performance, consistency, safety, convenience, functionality.</li><li>Native disadvantages: harder to build, approval process, hard to discover, fragmentation.</li><li>Mobile Web advantages: open, connected, ubiquitous, unregulated.</li><li>Mobile Web disadvantages: unregulated, performance is slower.</li><li>Mozilla and Google are coming out with web app stores soon.</li><li>Looking for a fixed header and footer for your scrollable window on iOS? Try <a href="http://cubiq.org/iscroll" target="new">iScroll</a>.</li></ul><p><em>There are clearly many advantages to Mobile Web. Its deployment model is instantaneous and it helps reduce fragmentation and development costs. If you are interested in jQuery Mobile, here is a good <a href="http://www.meetup.com/bostonphp/pages/Video_and_Resources_for_jQuery_Mobile/" target="new">presentation</a>.</em></p><br /><br /><h3>Blackberry Playbook</h3>Key points:<ul><li>Cost of entry</li><ul><li>BlackBerry: Free</li><li>Android: $25 (one time)</li><li>Apple: $99/yr</li></ul><li>Distribution</li><ul><li>Google: lass than hour</li><li>BlackBerry: 4-6 hours</li><li>Apple: up to 2 weeks</li></ul><li>BlackBerry has a "try before you buy" program in their AppWorld store which is unique.</li></ul><p><em>The Playbook tablet by BlackBerry will be released soon. While competition is good, they definitely have an uphill battle to climb.</em></p>Brad Broulikhttp://www.blogger.com/profile/14248585105589786365noreply@blogger.com0tag:blogger.com,1999:blog-7224548894465589501.post-88990536402589062892011-03-06T07:34:00.008-06:002011-03-07T21:36:19.948-06:00No Fluff Just Stuff: Minneapolis Summary (Spring)The <a href="http://www.nofluffjuststuff.com/home/main" target="new">No Fluff Just Stuff</a> conference was back in Minneapolis. Here is a brief recap of the sessions I attended:<br /><br /><img width="650" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_-W6Z8RN4BmywUAMEMiNC1Lo4WmGwOOY5cn5yMwQVghgCopruNQS0HVnQDfdo6iNwS2xmjyrQOyE5RAYlAnplBae4UaMlma7amDy9oadaZlmD-dz4_nqTMq6aI1EhaUaulkQvHicsyUMd/s1600/friday.jpg" alt="Friday Sessions" /><br /><br /><h3>NoSQL Smackdown!</h3>Key points:<ul><li>NoSQL is a set of different approaches to storing and retrieving data</li><li>Cassandra:</li><ul><li>Implementation Language: Java 6</li><li>Data Model: BigTable</li><li>Advantages: Highly scaleable</li><li>Deployments: facebook, twitter</li></ul><li>MongoDB:</li><ul><li>Implementation Language: C++</li><li>Data Model: JSON</li><li>Advantages: Simple</li><li>Deployments: sourceforge, bit.ly, shutterfly, Etsy</li></ul></ul><p><em>I like the fact MongoDB persists JSON. I implemented a similar solution on a recent mobile application where JSON objects were persisted on the client database. This solution is simple, requires minimal code, and provides great flexibility. If the domain model changes the database requires ZERO changes!</em></p><br /><br /><h3>Pragmatic Architecture</h3>Key points:<ul><li>Architects will be much more effective if they have a solid understanding of the business domain.</li><li>A good architecture makes it easy for developers to make "right" decisions.</li><li>Avoid architectures that were designed with Resume Driven Design (RDD).</li><li>Architects must be familiar with all technologies.</li><li>Prefer full-time architects or architects that at least see the consequences of their design after deploying to production.</li></ul><p><em>Over the past twelve years I have observed that the most effective architects code at least 49%. The more the better. If this isn't the case they simply won't be effective long term. The best architects produce working code in addition to their design artifacts.</em></p><br /><br /><h3>Introducing Spring Roo: From Zero to Working Spring Application in Record Time</h3>Key points:<ul><li>The rapid scaffolding is intriguing.</li><li>The de-Rooing is convenient when you need to remove the Roo footprint.</li><li>I particularly like the ability to replay your commands for project setup. Extract the Roo scripts and replaying them for the next project can offer rapid productivity.</li></ul><p><em>Roo makes sense for rapid prototyping or if you need to build an application in <a href="http://bradbroulik.blogspot.com/2011/02/agile-strategy-for-24-hour-coders.html" target="new">24-hours</a>.</em></p><br /><br /><img width="650" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi2Su3sxOkIVj0genLT8MkDF2N4IkHdndcAqqYUcMWbCr9Ga0fuSDLLGkvArApG99bh4De_S4vbt1RYyuKKhfsnpYq_U3GRH5s-HmO9M_efQ_yrK2N1uc7wWWlGSGNNP8-BPThx_2v6ktFf/s1600/saturday.jpg" alt="Saturday sessions" /><br /><br /><h3>HTML5: The JavaScript Parts</h3>Key points:<ul><li>Prefer <em>document.querySelector()</em> over <em>document.getElementById()</em> for much improved performance gains.</li><li>By default, <em>navigator.geolocation</em> is not enabled in Chrome. The user must explicitly enable it.</li><li><a href="http://www.modernizr.com/" target="new">Modernizr</a> can help with progressive enhancement by detecting browser feature availability and reacting appropriately.</li><li>Web Workers are not supported on mobile browsers:(</li><li><a href="http://caniuse.com/" target="new">When can I use...</a> shows feature compatibility by browser.</li><li><a href="http://fmbip.com" target="new">findmebyIP</a> shows feature support for your current browser.</li></ul><p><em>I predict a strong enterprise push for mobile web development starting this year. Mobile Web is device agnostic and jQuery Mobile will simplify the adoption. Even facebook has admitted to the pain, duplication, and cost of supporting five separate native apps.</em></p><br /><br /><h3>Developing Social-Ready Web Applications</h3>Key points:<ul><li>Enable twitter integration with <a href="http://dev.twitter.com/anywhere" target="new">@Anywhere</a></li><li>Dynamically search for tweets from twitter's restful API: <em>http://search.twitter.com/search.json?q=nfjs</em></li><li>Dynamically find the friends of a twitter user: <em>http://api.twitter.com/1/friends/ids.json?screen_name=<username></em></li><li>Enable facebook integration with their <a href="http://developers.facebook.com/docs/plugins/" target="new">Social Plugins</a></li><li>Dynamically retrieve the user info for a facebook user: <em>https://graph.facebook.com/<username></em></li><li>LinkedIn's developer <a href="http://developer.linkedin.com/community/widgets" target="new">widgets</a></li></ul><p><em>The greatest benefit of <a href="http://www.springsource.org/spring-social" target="new">Spring Social</a> is its simplification of SSO integration.</em></p><br /><br /><h3>Git Going with Distributed Version Control</h3>Key points:<ul><li>Git is 10-100 times faster than Subversion.</li><li>Git can bisect bugs or find the commit that broke a test.</li><li>Git can search (grep) the entire revision history without checkouts. For example, I can find a particular commit where a line was added.</li><li>Git persists the versioned artifacts to the file system as 40 character hashes. This approach is very lightweight and provides extremely fast compares.</li><li>Unlike Subversion, Git <em>does not</em> pollute the source directories with version control meta data.</li></ul><p><em>I have been working with Git for several weeks. Matthew's <a href="http://refcardz.dzone.com/refcardz/getting-started-git" target="new">DZone Refcard</a> about Git has also been a very valuable resource. Git was my favorite session so far!</em></p><br /><br /><img width="650" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiske4nhvHqJ5ASBIpuf51IjFj8CM0E9NmnIT2px-a18QctVB3CM2KTbK0e-2I6qNTB2BI90gDS_-3w6IxDXGIzCITnQS0DNz4wRVb1MOUILNgQk-braUzow4YFHBmDQCK2gzi9j9ukCAIp/s1600/sunday.jpg" alt="Sunday sessions" /><br /><br /><h3>jQuery: Ajax Made Easy</h3>Key points:<ul><li>jQuery has a small footprint, the code is clean, it has good documentation, has excellent CSS selector support, and it's the most popular.</li><li>If you only need the CSS selector support it's available in <a href="http://sizzlejs.com/" target="new">Sizzle</a>.</li></ul><p><em>If you want read well written JavaScript it is worth your time to checkout <a href="https://github.com/jquery/jquery" target="new">jQuery's source code.</a></em></p><br /><br /><h3>Going Mobile with jQuery</h3>Key points:<ul><li>50% of population will have smartphone by the end of 2011</li><li><a hef="http://jeromeetienne.github.com/jquery-mobile-960/" target="new">Tablet support</a> is now available</li><li>Favorite quote: "Using an iPhone during a meeting makes you look like a proactive employee." --Nathaniel Schutta</li></ul><p><em>I have been working with jQuery Mobile for several months now and it is a simple framework. The team is extremely eager to support nearly every device. After spending time learning Git, I am finally setup to help contribute to the project. Now I need to find the time to focus on a task, defect, or test! In addition to <a href="http://jquerymobile.com/" target="new">jQuery Mobile</a> there are also mobile web frameworks available from <a href="http://www.yuiblog.com/blog/2010/09/07/yui-3-2-0/" target="new">YUI</a>, <a href="http://www.jqtouch.com/" target="new">jQTouch</a>, and <a href="http://joapp.com/" target="new">Jo</a>.</em></p><br /><br /><h3>Code Craft</h3>Key points:<ul><li>We need to spend more time reading code.</li><li>There is nothing wrong with simplicity! Simple code == good</li><li>If you are having difficulties with code reviews try <a href="http://www.atlassian.com/software/crucible/" target="new">Crucible</a>.</li></ul><br /><br /><h3>Mobile GUI Frameworks</h3>Key points:<ul><li><a href="http://www.jqtouch.com/" target="new">jQTouch</a> is the smoothest mobile web framework on the market today. However, only iOS is able to reap many of these benefits.</li><li><a href="http://joapp.com/" target="new">Jo</a> is a new mobile framework I definitely want to look at closer. It's lighter weight than <a href="http://jquerymobile.com/" target="new">jQuery Mobile</a> and the end user codes primarily in JavaScript whereas jQuery Mobile is more markup driven.</li></ul>Brad Broulikhttp://www.blogger.com/profile/14248585105589786365noreply@blogger.com0tag:blogger.com,1999:blog-7224548894465589501.post-1925541299851198412011-02-14T17:24:00.004-06:002011-02-14T17:32:22.779-06:00Agile Strategy for 24-hour Coders Challenge<img style="border:0px; float:right; margin:-20px 0px -5px 10px; width: 135px; height:<br />135px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjzJV5WqqA_piwpWWSZrLZ97zp2TNQ3M6pM-O5jfG-qH21sw8HPk5sqTYgcbOwrp8PShYCHnbDqKCLu-yIh1oaBgMjdYLEE-l02smaDHlKdg34xedqze2SJvoXFWlGIsifGJIGk1MJJHCC6/s400/R_C_Wall_Clock.jpg" border="0" alt="clock" id="BLOGGER_PHOTO_ID_5571883970562858802" /><p>Can we build an app in twenty-four hours? We definitely can with the right planning and tools. The <a href="http://nerdery.com/" target="new">Nerdery</a> is organizing an <a href="http://tc2011.overnightwebsitechallenge.com/" target="new">overnight website challenge</a> in the Twin Cities where teams compete to build the best Web application within twenty-four hours. I have always watched these <a href="http://en.wikipedia.org/wiki/Food_Network_Challenge" target="new">competitions</a> on TV but never with software developers! What is the ideal strategy for this competition?</p><br /><br /><h3>Agile Planning Strategy</h3><p>Pre-competition planning is the most important step to success. Literally, every detail must be planned prior to competition. This will yield great efficiencies during the competition. Pre-competition planning considerations must include:</p><ul><li>Technology stack: Java, Grails, Rails, PHP</li><li>Tool suite: IDE, graphic editors</li><li>Source repository: GIT, shared local repository</li><li>Hosting platform: <a href="http://code.google.com/appengine/" target="new">Google App Engine</a> (GAE), local environment</li><li>Identify roles: developer, designer, QA, manager, presenter</li><li>Database: relational, NoSQL (GAE)</li><li>Scaffolding for rapid application development (RAD):<ul><li>Java: <a href="http://www.springsource.org/roo" target="new">Spring Roo</a></li><li>Groovy: <a href="http://www.grails.org/Scaffolding" target="new">Grails</a></li><li>Ruby: <a href="http://www.tutorialspoint.com/ruby-on-rails/rails-scaffolding.htm" target="new">Rails</a></li><li>PHP: <a href="http://www.phpscaffold.com/" target="new">phpScaffold</a></li></ul></li><li>Requirements gathering techniques: wireframes, use cases</li><li>Task management: whiteboard, electronic</li><li>Pre-designed templates for common functionality: Home page, dashboards, login, reports</li></ul><br /><br /><h3>Multiple Platform Strategy</h3><p>When I initially saw the 10 member team size I knew that could lead to inefficiencies. Often a team of 3-6 is an ideal size. To remedy this issue why not split the team into two smaller sub-groups? Have one group focus on a desktop Web solution and the other on a mobile Web solution. I see two huge advantages of this strategy. First, it should help eliminate critical paths. Again, with ten team members I see too many members waiting idle for others to complete dependent tasks. And most importantly, this strategy should help deliver that extra punch to help wow the judges.</p><br /><br /><h3>Execution Strategy</h3><p>Prior to the competition, every developer and designer must have a fully integrated development environment. Everyones environment must build a template project, sync to the repository, and deploy to the production-ready hosting environment. Once competition begins the focus must be solely about requirements gathering, task definition, prioritization, and efficient implementation.</p><br /><br /><h3>Kudos</h3><p>I wanted to thank the <a href="http://nerdery.com/" target="new">Nerdery</a> for hosting such a unique event in the Twin Cities! This is 100% voluntary and the solutions are built for non-profits!</p>Brad Broulikhttp://www.blogger.com/profile/14248585105589786365noreply@blogger.com0tag:blogger.com,1999:blog-7224548894465589501.post-32643728633183279762011-01-16T15:34:00.001-06:002011-01-16T15:43:31.743-06:00JavaScript Performance: Creational PatternsWhich JavaScript creational pattern is the most efficient? Is it an object literal, functional, or pseudoclassical object? With <a href="http://code.google.com/p/js-test-driver/" target="new">JsTestDriver</a>, I setup a quick performance test to gather the metrics. <br /><br /> <br /><h3>Creational Patterns to Test</h3>I tested the three most popular creational patterns in JavaScript. Object literals, functional objects, and pseudoclassical objects. In particular, I wanted to test their singleton and instance based execution times.<br /><pre><span class="code-highlight1">/*<br /> * Pseudoclassical object designed as a singleton<br /> */</span><br /><span style="color: rgb(0, 51, 102); font-weight:bold">var</span> PseudoclassicalSingleton = <span style="color: rgb(0, 51, 102); font-weight:bold">new function</span>(){<br /> <span style="color: rgb(0, 51, 102); font-weight:bold">this</span>.getName = <span style="color: rgb(0, 51, 102); font-weight:bold">function</span>(){<br /> <span style="color: rgb(0, 51, 102); font-weight:bold">return</span> <span style="color: rgb(51, 102, 204);">'Pseudoclassical'</span>;<br /> };<br />};<br /><br /><span class="code-highlight1">/*<br /> * Functional object designed as a singleton<br /> */</span><br /><span style="color: rgb(0, 51, 102); font-weight:bold">var</span> FunctionalSingleton = (<span style="color: rgb(0, 51, 102); font-weight:bold">function</span>(){<br /> <span style="color: rgb(0, 51, 102); font-weight:bold">return</span> {<br /> getName: <span style="color: rgb(0, 51, 102); font-weight:bold">function</span>(){<br /> <span style="color: rgb(0, 51, 102); font-weight:bold">return</span> <span style="color: rgb(51, 102, 204);">'Functional'</span>;<br /> }<br /> };<br />})();<br /><br /><span class="code-highlight1">/*<br /> * Object literal (implicitly singleton)<br /> */</span><br /><span style="color: rgb(0, 51, 102); font-weight:bold">var</span> ObjectLiteralSingleton = {<br /> getName: <span style="color: rgb(0, 51, 102); font-weight:bold">function</span>(){<br /> <span style="color: rgb(0, 51, 102); font-weight:bold">return</span> <span style="color: rgb(51, 102, 204);">'ObjectLiteral'</span>;<br /> }<br />};<br /><br /><span class="code-highlight1">/*<br /> * Pseudoclassical object designed as a non-singleton<br /> */</span><br /><span style="color: rgb(0, 51, 102); font-weight:bold">var</span> PseudoclassicalInstance = function(){<br /> <span style="color: rgb(0, 51, 102); font-weight:bold">this</span>.getName = <span style="color: rgb(0, 51, 102); font-weight:bold">function</span>(){<br /> <span style="color: rgb(0, 51, 102); font-weight:bold">return</span> <span style="color: rgb(51, 102, 204);">'Pseudoclassical'</span>;<br /> };<br />};<br /><br /><span class="code-highlight1">/*<br /> * Functional object designed as a non-singleton<br /> */</span><br /><span style="color: rgb(0, 51, 102); font-weight:bold">var</span> FunctionalInstance = function(){<br /> <span style="color: rgb(0, 51, 102); font-weight:bold">return</span> {<br /> getName: <span style="color: rgb(0, 51, 102); font-weight:bold">function</span>(){<br /> <span style="color: rgb(0, 51, 102); font-weight:bold">return</span> <span style="color: rgb(51, 102, 204);">'Functional'</span>;<br /> }<br /> };<br />};<br /></pre><br /><br /><h3>Performance Test Runner</h3>I setup <a href="http://code.google.com/p/js-test-driver/" target="new">JsTestDriver</a> to execute the test cases against Firefox. One advantage of JsTestDriver is I can run my test cases against <em>any</em> browser! Each performance test will instantiate its creational pattern 1.4 million times as indicated by the <em>RUN_TIMES</em> constant. Additionally, I ran the performance test suite twenty times to gather an adequate average. <br /><pre>CreationalPatternsPerformanceTest = TestCase(<span style="color: rgb(51, 102, 204);">"CreationalPatternsPerformanceTest"</span>);<br /><br /><span style="color: rgb(0, 51, 102); font-weight:bold">var</span> RUN_TIMES = <span style="color: rgb(51, 102, 204);">1400000</span>; <br /><br /><span style="color: rgb(0, 51, 102); font-weight:bold">var</span> performanceTest = <span style="color: rgb(0, 51, 102); font-weight:bold">function</span>(name, f){<br /> console.time(name);<br /> <span style="color: rgb(0, 51, 102); font-weight:bold">for</span> (<span style="color: rgb(0, 51, 102); font-weight:bold">var</span> i = <span style="color: rgb(51, 102, 204);">0</span>; i < RUN_TIMES; i++){<br /> f();<br /> }<br /> console.timeEnd(name);<br />};<br /><br />CreationalPatternsPerformanceTest.prototype.testResponseTimes = <span style="color: rgb(0, 51, 102); font-weight:bold">function</span>(){<br /> performanceTest(<span style="color: rgb(51, 102, 204);">"ObjectLiteral (singleton)"</span>, <span style="color: rgb(0, 51, 102); font-weight:bold">function</span>(){ <br /> ObjectLiteralSingleton.getName(); <br /> });<br /> performanceTest(<span style="color: rgb(51, 102, 204);">"Pseudoclassical (singleton)"</span>, <span style="color: rgb(0, 51, 102); font-weight:bold">function</span>(){ <br /> PseudoclassicalSingleton.getName(); <br /> });<br /> performanceTest(<span style="color: rgb(51, 102, 204);">"Functional (singleton)"</span>, <span style="color: rgb(0, 51, 102); font-weight:bold">function</span>(){ <br /> FunctionalSingleton.getName(); <br /> });<br /> performanceTest(<span style="color: rgb(51, 102, 204);">"Pseudoclassical (instance)"</span>, <span style="color: rgb(0, 51, 102); font-weight:bold">function</span>(){ <br /> new PseudoclassicalInstance().getName(); <br /> });<br /> performanceTest(<span style="color: rgb(51, 102, 204);">"Functional (instance)"</span>, <span style="color: rgb(0, 51, 102); font-weight:bold">function</span>(){ <br /> FunctionalInstance().getName(); <br /> });<br />};</pre><br /><br /><table><caption><strong>Performance Test Results</strong></caption><tr><th> </th><br /><th style="text-align:center;">Object Literal (singleton)</th><th style="text-align:center;">Pseudoclassical (singleton)</th><th style="text-align:center;">Functional (singleton)</th><th style="text-align:center;">Pseudoclassical (instance)</th><th style="text-align:center;">Functional (instance)</th></tr><tr class="alt"><th style="text-align:center;">Avg (ms)</th><td style="text-align:center; font-weight:bold; color:green">1093</td><td style="text-align:center; font-weight:bold; color:green">1095</td><td style="text-align:center; font-weight:bold; color:green">1094</td><td style="text-align:center; font-weight:bold; color:red">3301</td><td style="text-align:center; font-weight:bold; color:red">3341</td></tr><tr><th style="text-align:center;">Min (ms)</th><td style="text-align:center;">1070</td><td style="text-align:center;">1064</td><td style="text-align:center;">1062</td><td style="text-align:center;">3225</td><td style="text-align:center;">3246</td></tr><tr class="alt"><th style="text-align:center;">Max (ms)</th><td style="text-align:center;">1112</td><td style="text-align:center;">1114</td><td style="text-align:center;">1113</td><td style="text-align:center;">3460</td><td style="text-align:center;">3476</td></tr></table><br /><br /> <br /><h3>Areas of Interest</h3><br />I had three specific comparisons I wanted to evaluate heading into this experiment:<br /><ol><li><strong>Object literals vs the others (functional and pseudoclassical):</strong> I knew object literals were going to be the most efficient. However, I really wanted to see how <em>much faster</em> they really are. The result was surprising. Object literals (1093 ms) barely out performed functional (1094 ms) and pseudoclassical (1095 ms) response times. This difference is really negligible. Unlike object literals, functional and pseudoclassical objects can provide security. And the performance degradation is almost non-existent.</li><li><strong>Functional vs Pseudoclassical:</strong> There are always <a href="http://www.bolinfest.com/javascript/inheritance.php" target="new">debates</a> over these two objects. While I personally prefer pseudoclassical objects when security is a concern I definitely wanted to see which one was the more performant option. In the end, neither pattern distanced itself from the other in regards to performance.</li><li><strong>Singletons vs Instance based Objects:</strong> I knew singletons would be the most efficient. However, I wanted to see what this difference really was. On average, singletons were <em>three times</em> faster.</li></ol><br /><br /><h3>Lessons Learned</h3><ol><li>Prefer singletons over instance based objects when possible.</li><li>Prefer object literals when you are <em>not</em> concerned about privacy.</li><li>The performance metrics alone are not enough to sway the functional vs pseudoclassical debate.</li></ol>Brad Broulikhttp://www.blogger.com/profile/14248585105589786365noreply@blogger.com1