<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Ryan Taylor</title>
    <description>The latest articles on Forem by Ryan Taylor (@crtaylor243).</description>
    <link>https://forem.com/crtaylor243</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F489526%2Fc530d8fd-fed2-4b8c-8819-46d5d12af797.jpeg</url>
      <title>Forem: Ryan Taylor</title>
      <link>https://forem.com/crtaylor243</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/crtaylor243"/>
    <language>en</language>
    <item>
      <title>My Delivery Lead Journey</title>
      <dc:creator>Ryan Taylor</dc:creator>
      <pubDate>Thu, 13 Oct 2022 21:20:14 +0000</pubDate>
      <link>https://forem.com/focused_dot_io/my-delivery-lead-journey-2nda</link>
      <guid>https://forem.com/focused_dot_io/my-delivery-lead-journey-2nda</guid>
      <description>&lt;p&gt;Two years ago today, I joined &lt;a href="https://focusedlabs.io/" rel="noopener noreferrer"&gt;Focused Labs&lt;/a&gt; as a software developer. It was a time where the whole company could fit inside a modest conference room and everyone was writing code for client projects. The story of our growth since then deserves its own blog series, but suffice it to say that there has been a tremendous amount of change since October 2020. The same is true for me.&lt;/p&gt;

&lt;p&gt;Today I am announcing my transition to the Delivery Lead team, reporting to our Director of Delivery, &lt;a href="https://www.linkedin.com/in/rileylongo/" rel="noopener noreferrer"&gt;Riley Longo&lt;/a&gt;. As a software practitioner for over a decade, this is a significant change for me, and I’d like to share my motivations - and why I’m excited to help launch this practice within Focused Labs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Discovery&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I have been a history nut for as long as I can remember, so I jumped at the chance to take an AP-level US History in high school. It was an engaging class (thanks, Mr. Brewer!) and I would often create study guides for myself in preparation for our tests. At some point mid-semester, I shared one of my guides with a friend. The next day, I was surprised to find copies on a number of my schoolmates’ desks! It felt great knowing that my work could help others. Although I didn’t realize it at the time, this positive feedback loop would come to underpin my career as an engineer and consultant.&lt;/p&gt;

&lt;p&gt;I entered the professional workplace over a decade ago, and during that time I’ve written software for a number of industries and learned from incredible folks - especially the &lt;a href="https://focusedlabs.io/about" rel="noopener noreferrer"&gt;experts at Focused Labs&lt;/a&gt;. Our clients have high expectations for our work and trust us with high-value work, and we consistently deliver working software. Nothing beats a happy stakeholder!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Delivery&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Focused Labs has scaled tremendously over the past two years - there are 5x more people today than when I joined. Today, we work with world-class enterprises, innovative Web3 start-ups, pioneering restaurant groups, and &lt;a href="https://focusedlabs.io/case-studies" rel="noopener noreferrer"&gt;everything in between&lt;/a&gt;. That’s a &lt;em&gt;lot&lt;/em&gt; of working software delivered each week; enter the Delivery Lead team.&lt;/p&gt;

&lt;p&gt;Riley joined Focused in early 2022 and quickly started making meaningful changes in our ability to deliver. She forged close relationships with our client stakeholders to ensure our teams are aligned with their goals. Using the &lt;a href="https://engineering.atspotify.com/2014/09/squad-health-check-model/?share=facebook" rel="noopener noreferrer"&gt;Spotify Health Check&lt;/a&gt; as a guide, she gave teams a new feedback loop to invest in long-term success. Traditionally, some of these responsibilities would be divided between the project’s technical anchor, product managers, and the sales team. With an official Delivery representative, each of those folks can stay heads-down on their respective practice - and deliver like crazy!&lt;/p&gt;

&lt;p&gt;I have anchored a number of projects at Focused Labs and discovered along the way that I feel at my best when building trusted relationships and clearing the way for my talented colleagues to deliver. Through a series of virtual coffee chats with Riley, I discovered that a move to the Delivery Lead team would give me the opportunity to perform these duties at a more strategic level for Focused Labs. When the team was officially launched this fall, I joined without hesitation!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Future&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Although it sometimes feels like I’m drinking from a firehose, my experience as a Delivery Lead thus far has reinforced my decision to step away from the practice of software development and into project leadership. Our clients are awesome, and I’ve enjoyed working closer with them to keep our teams building the highest-value software. The Focused Labs team is incredible, and I’m in awe of their ability to crush code. Our team is building processes and tools that will unlock scale, promote psychological safety, and give our teams the platform to practice their craft. &lt;/p&gt;

&lt;p&gt;In some ways, this role is an opportunity for me to capitalize on that great feeling I got back in high school - creating value by enabling others to succeed. As much as I enjoy writing code, it’s far more satisfying for me to see our teams operate with less friction and with full confidence that they are building something that is useful and desired. I am excited for the future, and I suspect I’ll have a lot to share on my 3-year anniversary!&lt;/p&gt;

&lt;p&gt;The Delivery Lead team is excited to offer our services! Are you interested in...&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A team health check?&lt;/li&gt;
&lt;li&gt;Working agreements for a new (or established team)?&lt;/li&gt;
&lt;li&gt;Outcome-based metrics for your project?&lt;/li&gt;
&lt;li&gt;A psychological safety assessment?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If so, get in touch via &lt;a href="//mailto:delivery@focusedlabs.io"&gt;email&lt;/a&gt; or find us on &lt;a href="https://www.linkedin.com/company/build-focused-labs/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;!&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Cover photo is my own: &lt;a href="https://flic.kr/p/2eZQW72" rel="noopener noreferrer"&gt;Flickr&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>career</category>
      <category>leadership</category>
    </item>
    <item>
      <title>What Can a Hackathon Do for your Technical Organization?</title>
      <dc:creator>Ryan Taylor</dc:creator>
      <pubDate>Wed, 12 May 2021 14:26:37 +0000</pubDate>
      <link>https://forem.com/focused_dot_io/what-can-a-hackathon-do-for-your-technical-organization-omf</link>
      <guid>https://forem.com/focused_dot_io/what-can-a-hackathon-do-for-your-technical-organization-omf</guid>
      <description>&lt;p&gt;Broadly-speaking, a &lt;em&gt;hackathon&lt;/em&gt; is an event that temporarily disrupts "normal" business operations and allows self-organized teams to rapid prototype around their interest areas (technical discovery, business problems, future-looking improvements, etc). Organizations like Atlassian have formal hackathons (&lt;a href="https://www.atlassian.com/company/shipit" rel="noopener noreferrer"&gt;ShipIt Days&lt;/a&gt;) and others take an ad hoc approach. Rather than re-state what has already been written about successfully planning and holding these events, I would like to share a consultant's perspective on how teams benefit from special hacking events.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Build Competency in Incremental Delivery&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As a practitioner of &lt;a href="https://en.wikipedia.org/wiki/Extreme_programming" rel="noopener noreferrer"&gt;Extreme Programming&lt;/a&gt;, I constantly seek to improve responsiveness by shortening feedback loops. In a well-managed hackathon, the entire team is held to a deadline to deliver results; this prompts each team to regularly assess (and adjust) their scope of work to match a target pace of delivery. During presentations, teams can evaluate their contribution relative to their peer teams, providing even more feedback. As this cycle is typically much shorter than "normal" operations (1 day hackathon vs a 1 week sprint), the team will be able to learn and grow more quickly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Connect to Stakeholders&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I love this new functionality! I can use it to solve [&lt;em&gt;some nagging business problem&lt;/em&gt;]!"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;By requiring that hackathon teams demonstrate their work in a closing ceremony, participants sees the organization from a stakeholder's perspective. This rapid feedback informs each developer's future work, and product owners will take more innovative risks with the knowledge that their technical team has the capacity to iterate quickly. This is a cornerstone of high-trust teams.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Break Down Silos&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Allowing teams to self-organize gives them the opportunity to network and collaborate outside their "normal" domain. As a consultant, I am often empowered to cross team lines (especially during &lt;em&gt;&lt;a href="https://tanzu.vmware.com/content/blog/what-we-did-on-our-summer-internship-at-pivotal-discovery-and-framing" rel="noopener noreferrer"&gt;Discovery and Framing&lt;/a&gt;&lt;/em&gt;), and I can attest to the power of reaching across team boundaries. This effect compounds when teams find synergistic opportunities across functional boundaries. After all, when product and engineering work directly together, the risk of expensive rework drops considerably. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Gift of Shared Experience&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Even in the most tight-knit teams, daily work experiences can be wildly different (DevOps vs Customer Success, for example). Eventually, these functional groups begin to diverge and develop their own work subcultures. Intentionally bringing the team together for a shared set of rituals (coffee and donuts during kick-off, mid-hackathon trivia, project demos, and a hearty round of appreciation and congrats) can stitch the teams back together.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Demonstrate Your Values&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Is your company &lt;em&gt;customer-obsessed&lt;/em&gt;? Does it &lt;em&gt;cultivate a learning-oriented culture&lt;/em&gt;? Do you &lt;em&gt;work hard, play hard&lt;/em&gt;? No matter how Core Values are expressed on a corporate landing page, the actual results of a hackathon will open a window into the team's inner dynamics. This is important feedback for organizational leaders, who experience the team differently than their reports. Seeing the team in a different light can be instructive when mapping out the future of a company.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;PS: On "normal"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In this article, I want to provide an argument for reconsidering what is "normal" for a team (of any size). Across multiple successful hackathons, I've seen teams produce surprising results in a very short period of time. I believe that a small change in environment (aka: turn on email auto-responders, silence the phones until 4pm, sit with your new team in a new conference room, etc) can have significant positive externalities. With some practice, teams can hold Retrospectives and incorporate hackathon tactics into their daily work, turning the static "normal" to a continuously-improving &lt;em&gt;norm.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Cover photo is my own: &lt;a href="https://flic.kr/p/2ibgDJ3" rel="noopener noreferrer"&gt;Flickr&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>hackathon</category>
      <category>culture</category>
      <category>consulting</category>
    </item>
    <item>
      <title>Faster .NET CI/CD pipelines with test filters</title>
      <dc:creator>Ryan Taylor</dc:creator>
      <pubDate>Fri, 15 Jan 2021 15:26:08 +0000</pubDate>
      <link>https://forem.com/focused_dot_io/faster-net-ci-cd-pipelines-with-test-filters-1k6o</link>
      <guid>https://forem.com/focused_dot_io/faster-net-ci-cd-pipelines-with-test-filters-1k6o</guid>
      <description>&lt;p&gt;Sluggish integration and delivery pipelines leave developers vulnerable to distraction.  If your unit and integration tests live in one &lt;code&gt;.csproj&lt;/code&gt; and run in both CI and CD, tune your pipeline stages to run only relevant tests.&lt;/p&gt;

&lt;h4&gt;
  
  
  The Tests
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Fact&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Trait&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Category"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;"Unit"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;ThisTest_ShouldBeAUnitTest&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; 
&lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="c1"&gt;// Useful unit test&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; 

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Fact&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Trait&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Category"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;"Integration"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;WhenThisThingDoesX_ThatThingShouldDoY&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; 
&lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="c1"&gt;// Useful integration test&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Tuning the Pipelines
&lt;/h4&gt;

&lt;p&gt;To run only unit tests in CI:&lt;br&gt;
&lt;code&gt;dotnet test --filter Category=Unit&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;To run only integration tests in CD:&lt;br&gt;
&lt;code&gt;dotnet test --filter Category=Integration&lt;/code&gt;&lt;/p&gt;




&lt;p&gt;Photo credit: &lt;a href="https://www.flickr.com/photos/andrewparnell/2738598951/in/photolist-5b134i-8BnKfE-8BjGfx-8BnKBq-8BnK8u-8BnLWo-8BnKqm-8NE8Ac-ry4zJx-2hcjGDn-bsM6Ux-bpe3u2-bvf1tF-bped7T-bveZb2-bveZNX-bveSMD-bsLYKt-adbMBJ-6kN7G-5UnKjd-5VCYtc-AEJUs-gQJMj-bk1bNK-udPXub-AEJkJ-vaQKS2-AEK23-uTgm7G-AEJvf-v8wAgs-bmNiUT-uToGzc-vakqGU-vak6Xj-vb3LXa-UutpCt-6pSjdF-bpe8JT-U9zyQn-5fq7Qu-28Kj4-5fkJXp-28KgA-kTdV5-6dLnTT-5fq8b3-6dLotz-5fq7ES" rel="noopener noreferrer"&gt;Andrew Parnell&lt;/a&gt;&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>bloggolf</category>
      <category>cicd</category>
      <category>testing</category>
    </item>
    <item>
      <title>Focused On: Culture</title>
      <dc:creator>Ryan Taylor</dc:creator>
      <pubDate>Thu, 19 Nov 2020 17:53:18 +0000</pubDate>
      <link>https://forem.com/focused_dot_io/focused-on-culture-22ef</link>
      <guid>https://forem.com/focused_dot_io/focused-on-culture-22ef</guid>
      <description>&lt;p&gt;At &lt;a href="https://focusedlabs.io/" rel="noopener noreferrer"&gt;Focused Labs&lt;/a&gt;, we hold a weekly &lt;em&gt;Culture Lunch&lt;/em&gt; to host important conversations about our work life. Although our company still has its youth, the seasoned team can draw from a treasure chest of experience - and it's important that we share this kind of wealth. &lt;/p&gt;

&lt;p&gt;About a month ago, &lt;a href="https://www.linkedin.com/in/austinbv/" rel="noopener noreferrer"&gt;Austin&lt;/a&gt; set the stage for a conversation to define our own work culture. &lt;/p&gt;

&lt;p&gt;It's hard not to adopt an aspirational tone when writing your culture down (especially for the outside world). Conversations, though, draw out the truth in increments, allowing everyone to contribute their experience. Reading back on our notes from the conversation, I'm noticing that the written product of our brainstorm carries both &lt;em&gt;aspiration&lt;/em&gt; &lt;strong&gt;and&lt;/strong&gt; &lt;em&gt;present quality&lt;/em&gt; - we set a high standard of quality, but aren't afraid to admit that we have to be constantly improving to maintain that standard.&lt;/p&gt;

&lt;p&gt;Having recently joined Focused Labs, this was a critical early opportunity for me to hear the values of my new colleagues directly - and contribute my own. Building great relationships takes time and trust, and this conversation gave me a foundation of understanding to start with. I think the approach of openly mapping your culture can be incredibly valuable, but it is hard to earn the trust that it requires. I hope that by sharing our experience, it can be a guidepost for others - and a force of accountability for us.&lt;/p&gt;

&lt;p&gt;-&lt;a href="https://www.linkedin.com/in/ryantaylor3/" rel="noopener noreferrer"&gt;Ryan&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Below is the product of our 10/19/2020 brainstorm about Focused Labs culture, lightly edited for readability.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Why We Do What We Do?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;❤️  Love your craft&lt;br&gt;
👂  Listen First&lt;br&gt;
🧠  Learn Why&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;em&gt;What is our&lt;/em&gt; &lt;strong&gt;culture&lt;/strong&gt;?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;We build our interactions around collaboration&lt;/li&gt;
&lt;li&gt;We deliver efficiently and fast&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;em&gt;What defines our&lt;/em&gt; &lt;strong&gt;culture&lt;/strong&gt;?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The people&lt;/li&gt;
&lt;li&gt;Daily practices&lt;/li&gt;
&lt;li&gt;Shared values around work output&lt;/li&gt;
&lt;li&gt;Our development approach &amp;amp; methodology&lt;/li&gt;
&lt;li&gt;How we communicate&lt;/li&gt;
&lt;li&gt;That we have clients&lt;/li&gt;
&lt;li&gt;Our mission&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;em&gt;What does it mean to&lt;/em&gt; &lt;strong&gt;love your craft&lt;/strong&gt;?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;I take enjoyment out of what I do&lt;/li&gt;
&lt;li&gt;I stay current on the latest and greatest&lt;/li&gt;
&lt;li&gt;You try to learn new things in your industry&lt;/li&gt;
&lt;li&gt;You can recognize bad craftsmanship&lt;/li&gt;
&lt;li&gt;You improve your approach&lt;/li&gt;
&lt;li&gt;You represent and showcase the value in quality&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;em&gt;Why do we say&lt;/em&gt; &lt;strong&gt;craft&lt;/strong&gt;?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;It makes it an art form&lt;/li&gt;
&lt;li&gt;Crafts can be continuously improved&lt;/li&gt;
&lt;li&gt;There are skills that are honed but never mastered&lt;/li&gt;
&lt;li&gt;It is more than just specific technologies&lt;/li&gt;
&lt;li&gt;Love your craft and then use the tools &lt;em&gt;(rather than loving your tools and then using the craft)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;There's passion and dedication to quality&lt;/li&gt;
&lt;li&gt;I feel accountable for the output&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;em&gt;What tools are integral to our&lt;/em&gt; &lt;strong&gt;culture&lt;/strong&gt;?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.builtinchicago.org/2020/10/30/pairing-strategies" rel="noopener noreferrer"&gt;Pair Programming&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Test driven development&lt;/li&gt;
&lt;li&gt;CI/CD&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Google&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Cover photo is my own: &lt;a href="https://www.flickr.com/photos/rasputin243/42025525155" rel="noopener noreferrer"&gt;Flickr&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>culture</category>
      <category>startup</category>
      <category>growth</category>
    </item>
    <item>
      <title>Cross-Parameter Validation with Spring</title>
      <dc:creator>Ryan Taylor</dc:creator>
      <pubDate>Tue, 03 Nov 2020 06:18:00 +0000</pubDate>
      <link>https://forem.com/focused_dot_io/cross-parameter-validation-with-spring-3955</link>
      <guid>https://forem.com/focused_dot_io/cross-parameter-validation-with-spring-3955</guid>
      <description>&lt;p&gt;With &lt;a href="https://spring.io/" rel="noopener noreferrer"&gt;Spring&lt;/a&gt;, data validation is a breeze in many common use cases (like validating a method's input parameters) - and is highly recommended for creating robust applications. &lt;/p&gt;

&lt;p&gt;Often, developers are called upon to produce software with more complex validation requirements. This guide describes one such situation and proposes a solution that is effective and simple to maintain. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Visit &lt;a href="https://github.com/crtaylor243/spring-cross-parameter-validation" rel="noopener noreferrer"&gt;this GitHub repo&lt;/a&gt; for a working example of the article's concepts. For a full technical guide for creating cross-parameter validations, refer to this &lt;a href="https://docs.jboss.org/hibernate/validator/4.1/reference/en-US/html/validator-customconstraints.html" rel="noopener noreferrer"&gt;JBoss guide&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Business Case
&lt;/h2&gt;

&lt;p&gt;Suppose you are developing an e-commerce application where users can request for a bouquet of flowers to be delivered to an address within a specific window of time. An order POJO might take the shape of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;io.focusedlabs.crossparametervalidation&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.time.ZonedDateTime&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;lombok.Builder&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;lombok.Data&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;@Builder&lt;/span&gt;
&lt;span class="nd"&gt;@Data&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FlowerDeliveryOrder&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
   &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;orderId&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
   &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;recipientName&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
   &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;ZonedDateTime&lt;/span&gt; &lt;span class="n"&gt;orderPlaced&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;  
   &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;ZonedDateTime&lt;/span&gt; &lt;span class="n"&gt;deliveryStart&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
   &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;ZonedDateTime&lt;/span&gt; &lt;span class="n"&gt;deliveryEnd&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Here we are using &lt;a href="https://projectlombok.org/" rel="noopener noreferrer"&gt;Lombok's&lt;/a&gt; &lt;code&gt;@Data&lt;/code&gt; and &lt;code&gt;@Builder&lt;/code&gt; annotations to auto-generate boilerplate code, saving keystrokes and valuable developer time.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Simple Validations
&lt;/h2&gt;

&lt;p&gt;The Bean Validation API can be used to enforce some simple and common validations, such as guaranteeing a &lt;code&gt;FlowerDeliveryOrder&lt;/code&gt; will have a non-null &lt;code&gt;orderId&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;io.focusedlabs.crossparametervalidation&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.time.ZonedDateTime&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;javax.validation.constraints.NotNull&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;javax.validation.constraints.NotEmpty&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;lombok.Builder&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;lombok.Data&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;@Builder&lt;/span&gt;
&lt;span class="nd"&gt;@Data&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FlowerDeliveryOrder&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
   &lt;span class="nd"&gt;@NotNull&lt;/span&gt;
   &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;orderId&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

   &lt;span class="nd"&gt;@NotEmpty&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Recipient Name must be specified"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
   &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;recipientName&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
   &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;ZonedDateTime&lt;/span&gt; &lt;span class="n"&gt;orderPlaced&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;  
   &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;ZonedDateTime&lt;/span&gt; &lt;span class="n"&gt;deliveryStart&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
   &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;ZonedDateTime&lt;/span&gt; &lt;span class="n"&gt;deliveryEnd&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;With this simple validation in place, flower delivery orders with an empty &lt;code&gt;recipientName&lt;/code&gt; will result in a thrown &lt;a href="https://docs.oracle.com/javaee/7/api/javax/validation/ConstraintViolationException.html" rel="noopener noreferrer"&gt;ConstraintViolationException&lt;/a&gt; that can be handled gracefully by the application.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Cross-Parameter Validations
&lt;/h2&gt;

&lt;p&gt;The simple validations above are useful for our application, but what if we want to validate business logic that depends on multiple fields? For example, if the user of the web form selects a &lt;code&gt;deliveryStart&lt;/code&gt; that is after the &lt;code&gt;deliveryEnd&lt;/code&gt;, the order should not be accepted. &lt;/p&gt;

&lt;p&gt;It might be tempting to implement an &lt;code&gt;isValid()&lt;/code&gt; method inside the &lt;code&gt;FlowerDeliveryOrder&lt;/code&gt; and manually check the result wherever the orders are processed. This may be appropriate sometimes, but the developers must remember to include the check everywhere the order is processed.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Instead, we can introduce a custom validation that inspects both parameters and integrates seamlessly with Spring's bean validation mechanism.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Operating Principle
&lt;/h2&gt;

&lt;p&gt;There are two parts to this solution: 1) a custom annotation to mark the class for validation and 2) a custom validator that encapsulates the business logic to be executed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Custom Annotation
&lt;/h3&gt;

&lt;p&gt;An annotation is a form of interface and are declared in a similar fashion. Below, we create a custom annotation that can adorn our &lt;code&gt;FlowerDeliveryOrder&lt;/code&gt; class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;io.focusedlabs.crossparametervalidation&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.lang.annotation.ElementType&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.lang.annotation.RetentionPolicy&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.lang.annotation.Retention&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.lang.annotation.Target&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;javax.validation.Constraint&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;javax.validation.Payload&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;@Target&lt;/span&gt;&lt;span class="o"&gt;({&lt;/span&gt;&lt;span class="nc"&gt;ElementType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;TYPE&lt;/span&gt;&lt;span class="o"&gt;})&lt;/span&gt;
&lt;span class="nd"&gt;@Retention&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;RetentionPolicy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;RUNTIME&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@Constraint&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;validatedBy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OrderDeliveryWindowValidator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nd"&gt;@interface&lt;/span&gt; &lt;span class="nc"&gt;ValidDeliveryWindow&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
   &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;message&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="s"&gt;"Delivery Window Start Time must precede End Time"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
   &lt;span class="nc"&gt;Class&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;?&amp;gt;[]&lt;/span&gt; &lt;span class="n"&gt;groups&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="o"&gt;{};&lt;/span&gt;
   &lt;span class="nc"&gt;Class&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;?&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Payload&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;[]&lt;/span&gt; &lt;span class="nf"&gt;payload&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="o"&gt;{};&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;In this configuration, the annotation can be placed on classes with &lt;code&gt;@Target({ElementType.TYPE})&lt;/code&gt; and is available to the application at runtime with &lt;code&gt;@Retention(RuntimePolicy.RUNTIME)&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The Bean Validation API requires &lt;code&gt;message&lt;/code&gt; and &lt;code&gt;groups&lt;/code&gt; at a minimum; we can add a &lt;code&gt;payload&lt;/code&gt; field to attach custom data to the constraint.  &lt;/p&gt;

&lt;h3&gt;
  
  
  Custom Validator
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;@Constraint&lt;/code&gt; annotation specifies a class that should be used by the Bean Validation API to perform the custom logic. This class must implement the &lt;code&gt;ConstraintValidator&lt;/code&gt; interface.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;io.focusedlabs.crossparametervalidation&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;javax.validation.ConstraintValidator&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;javax.validation.ConstraintValidatorContext&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrderDeliveryWindowValidator&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;ConstraintValidator&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ValidDeliveryWindow&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;FlowerDeliveryOrder&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ValidDeliveryWindow&lt;/span&gt; &lt;span class="n"&gt;constraintAnnotation&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;isValid&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;FlowerDeliveryOrder&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;ConstraintValidatorContext&lt;/span&gt; &lt;span class="n"&gt;constraintContext&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;deliveryStart&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isBefore&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;deliveryEnd&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Notice that the &lt;code&gt;isValid()&lt;/code&gt; method is very similar to how a developer might write an inline validation. This is great news, because it means that existing validations be easily refactored to use this pattern.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Finishing Up
&lt;/h3&gt;

&lt;p&gt;With the building blocks in place, we can now apply this custom validation to our &lt;code&gt;FlowerDeliveryOrder&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;io.focusedlabs.crossparametervalidation&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.time.ZonedDateTime&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;javax.validation.constraints.NotNull&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;javax.validation.constraints.NotEmpty&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;lombok.Builder&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;lombok.Data&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;@Builder&lt;/span&gt;
&lt;span class="nd"&gt;@Data&lt;/span&gt;
&lt;span class="nd"&gt;@ValidDeliveryWindow&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FlowerDeliveryOrder&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
   &lt;span class="nd"&gt;@NotNull&lt;/span&gt;
   &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;orderId&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

   &lt;span class="nd"&gt;@NotEmpty&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Recipient Name must be specified"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
   &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;recipientName&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
   &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;ZonedDateTime&lt;/span&gt; &lt;span class="n"&gt;orderPlaced&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;  
   &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;ZonedDateTime&lt;/span&gt; &lt;span class="n"&gt;deliveryStart&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
   &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;ZonedDateTime&lt;/span&gt; &lt;span class="n"&gt;deliveryEnd&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Parting Thoughts
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;While there is some initial time investment required to create a custom annotation and validator, change management becomes much easier from that point forward - and the code can be understood by developers at most skill levels. It is a trusted tool at &lt;a href="https://focusedlabs.io/" rel="noopener noreferrer"&gt;Focused Labs&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;Cover photo is my own: &lt;a href="https://www.flickr.com/photos/rasputin243/49308688351" rel="noopener noreferrer"&gt;Flickr&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>java</category>
      <category>tutorial</category>
      <category>oop</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
