<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://www.madebylotus.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://www.madebylotus.com/" rel="alternate" type="text/html" /><updated>2026-09-29T21:02:06+00:00</updated><id>https://www.madebylotus.com/feed.xml</id><title type="html">Lotus Apps</title><entry><title type="html">How we built an offline mapping mobile app for Windrock Park</title><link href="https://www.madebylotus.com/blog/how-we-built-an-offline-mapping-mobile-app-for-windrock-park/" rel="alternate" type="text/html" title="How we built an offline mapping mobile app for Windrock Park" /><published>2019-04-29T00:00:00+00:00</published><updated>2019-04-29T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/how-we-built-an-offline-mapping-mobile-app-for-windrock-park</id><content type="html" xml:base="https://www.madebylotus.com/blog/how-we-built-an-offline-mapping-mobile-app-for-windrock-park/"><![CDATA[<p>Windrock wanted to offer an offline capable, interactive, and updatable map resource through In-App Purchases to park guests; saving on print costs and offering customers what they want.</p>

<p>The largest ATV park in the South East is now offering native mobile apps for Android and iOS allowing park guests to download the map and use without any data connection while on the park trails.</p>

<p><img width="1360" height="1020" src="/static/images/posts/how-we-built-an-offline-mapping-mobile-app-for-windrock-park/windrock-legos.4ae344a301.webp" class="img-responsive" alt="The Windrock Park trail map app on an iPhone, surrounded by Lego bricks"></p>

<h2>The Challenge</h2>

<p>Building a mobile app for Android and iOS that ties seamlessly with the Windrock brand and experience, offering in app purchases to unlock the map functionality was a tall order. Cellular data is almost unavailable at the park, park guests want the map to be responsive, detailed, and always available: we had our work cut out for us.</p>

<h2>The Solution</h2>

<p>We took the opportunity to educate and inform the team at Windrock on the app distribution process, monetization strategies, maintenance, and other pitfalls as they become first time app creators and publishers.</p>

<p><a href="/portfolio/windrock-park/">Read more in our case study how we guided the great folks</a> at the Windrock team through the entire process of app creation, allowing them to launch a product their customer's absolutely love!</p>]]></content><author><name>Matt</name></author><category term="business" /><summary type="html"><![CDATA[Windrock wanted to offer an offline capable, interactive, and updatable map resource through In-App Purchases to park guests; saving on print costs and offering customers what they want.]]></summary></entry><entry><title type="html">3 Common Mistakes We See When Clients Skip Discovery</title><link href="https://www.madebylotus.com/blog/3-common-mistakes-we-see-when-clients-skip-discovery/" rel="alternate" type="text/html" title="3 Common Mistakes We See When Clients Skip Discovery" /><published>2019-04-24T00:00:00+00:00</published><updated>2019-04-24T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/3-common-mistakes-we-see-when-clients-skip-discovery</id><content type="html" xml:base="https://www.madebylotus.com/blog/3-common-mistakes-we-see-when-clients-skip-discovery/"><![CDATA[<p>For those new to software creation especially, the concept of spending significant effort defining and refining requirements and exploring the market, business fit and product idea before designing the first view can be frustrating.</p>

<p>However, discovery is a very important component to the success of any software project, allowing you to uncover important truths, priorities, features and audiences to guide the design and development efforts later.</p>

<p>What are some things that can go wrong if you skip this essential planning and research step at the beginning of your project you ask, well read on!</p>

<h2>Problem 1: Lack of Direction</h2>

<p>Most often we find projects without a discovery and research phase lack a clear direction. We don't mean that there are no goals; quite the contrary, often the goals are too large to easily meet or there are too many of them.</p>

<p>Often we see projects unable to clearly define what is important to the product and success of the project, leading to ineffective decision making, slipping timelines, confusing requirements and more.</p>

<p>A simpler side effect can be that there is simply no "point person" available, or leadership. Quite often through discovery natural leaders emerge: people with passion for the product, someone with the most understanding of the target audience needs and wants, etc. This person not know they are the natural product owner at the time, but the discovery process aids in uncovering a leader to keep the product interests at heart and make decisions.</p>

<h2>Problem 2: Designs That Solve No Problem</h2>

<p>You're making progress because you're seeing designs being created, everyone is excited to have some tangible progress. Right? Wrong!</p>

<p>Without proper research and planning, you may be designing the wrong thing! Considerations such as ideal screen size, emotions you want to convey, level of clarity needed in the product, or even if the product should be built for web or mobile: there are many complex and important decisions to make before ever designing the very first screen.</p>

<p>We often find that had the project been afforded proper research and planning via discovery, that important considerations for the target audience preferences and branding would have led in a different direction.</p>

<h2>Problem 3: Feature Creep</h2>

<p>Don't get us wrong, most projects uncover new features and functionality as the project progresses: feedback from testers, exposure to a larger team, and greater understanding of everyone involved generally compound the issue.</p>

<p>However, lacking discovery, we often see projects experience a hefty amount of feature creep due to a lack of understanding the full scope of the project at the onset.</p>

<p>This leads to features that have to be rushed or cut from the product entirely due to timeline and budget constraints, or even less than ideal solutions to the problem due to now existent constraints in the product that could have been solved more elegantly had all the information been known at the beginning before starting development.</p>]]></content><author><name>Matt</name></author><category term="entrepreneurship" /><category term="business" /><summary type="html"><![CDATA[For those new to software creation especially, the concept of spending significant effort defining and refining requirements and exploring the market, business fit and product idea before designing the first view can be frustrating.]]></summary></entry><entry><title type="html">Linking your Rails App to Salesforce with OAuth 2.0</title><link href="https://www.madebylotus.com/blog/linking-your-rails-app-to-salesforce-with-oauth-2.0/" rel="alternate" type="text/html" title="Linking your Rails App to Salesforce with OAuth 2.0" /><published>2019-04-17T00:00:00+00:00</published><updated>2019-04-17T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/linking-your-rails-app-to-salesforce-with-oauth-2.0</id><content type="html" xml:base="https://www.madebylotus.com/blog/linking-your-rails-app-to-salesforce-with-oauth-2.0/"><![CDATA[<p>Salesforce is one of the most widely used Customer Relationship Management (CRM) tools on the web today, and collects valuable data-driven insights for many businesses as they strive for growth. When building tailored software solutions for your business, it’s often a good idea to pull that valuable data you’ve worked so hard to obtain into your application to generate increasingly valuable data tooling.</p>

<h3>Creating a Connected App</h3>

<p>To connect your app to Salesforce Online, the first thing you need to do is go to <a href="https://developer.salesforce.com/signup">https://developer.salesforce.com/signup</a> and create a developer account. This is required in order to obtain a client ID and client secret, which you will need to securely connect your application to your salesforce account.</p>

<p>The next step is to create a Connected App in your newly-created salesforce account. Search for “App Manager” in the Quick Find search bar in the top left. After you enter the App Manager, you’ll need to click “New Connected App” in the top right.</p>

<p>There are a lot of optional fields here, but you must input your application name and application email. API name will be automatically generated if you don’t choose to specify it.</p>

<p>Under “API (Enable OAuth Settings)”, be sure to check the “Enable Oauth Settings” box, and more input fields will appear. This is where you will need to specify your callback URL and OAuth Scopes.</p>

<h3>Callback URL</h3>

<p>The callback url is where salesforce will send a response after the OAuth authorization endpoint receives your application’s request. This url can be anything you like but it needs to match a route you create in your rails application. In my application, I set up a salesforce session controller with the callback url going to a “callback” action. The routes looked like this:</p>

<script src="https://gist.github.com/vananreed/8459dd3fb73a5aa236c0a65d51ff032f.js"></script>

<p>Additionally, Salesforce requires the callback URL to be an encrypted link with SSL. I found this <a href="https://madeintandem.com/blog/rails-local-development-https-using-self-signed-ssl-certificate/">article</a> very helpful to set up a local SSL certificate that I could run for development in my local machine.</p>

<p>And consequently, the callback URL I inputted in Salesforce for development purposes was https://localhost:3000/salesforce/sessions/callback, and can be set dynamically in our application using the rails built in url helper: salesforce_session_url.</p>

<h3>OAuth Scopes</h3>

<p>This will be different for every app, and you need to decide what your application needs. For my purposes, I needed access to basic account info, and to have the ability to pull data from salesforce and add it to my PosgreSQL database. So, I selected:</p>

<p><em>“Access and Manage your data”</em></p>

<p><em>“Access your basic information”</em></p>

<p><em>“Perform requests on your behalf at any time”</em></p>

<p>Now, you can click “Save” and your consumer key and consumer secret will be created by Salesforce and visible in the Connected App details.</p>

<p>For more detailed instructions on setting up a Salesforce Connected App, visit this <a href="https://help.salesforce.com/articleView?id=connected_app_create.htm&amp;type=5">link</a>.</p>

<h3>Getting an Access Token</h3>

<p>Now that the Connected App has been created, we can head to our Rails app and build the OAuth 2.0 flow.</p>

<p>First, you need to save your client id and client secret into your app secrets. In config/secrets.yml, make sure you have:</p>

<script src="https://gist.github.com/vananreed/c5dfb5a9906a5946b4524b8f372ba426.js"></script>

<p>Obviously, you will need to add these keys to your production environment on Heroku (or whichever service you are using) as well.</p>

<p>Next, you can move to your controller to start making http requests to the necessary Salesforce API endpoints which can be viewed <a href="https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/intro_understanding_oauth_endpoints.htm">here</a>. In my salesforce sessions controller, I created a new OAuth2 Client/Consumer and made a request to the authorization endpoint with the required parameters:</p>

<script src="https://gist.github.com/vananreed/6f6d38ad3a2d7454064564566c090e2b.js"></script>

<p>You’ll want to add this link to your view file for the user to initiate the link:</p>

<script src="https://gist.github.com/vananreed/0fc552f0b86c6e6868bdb4203ea8ca79.js"></script>

<p>If you start your dev server, go into your browser, and click the link you will be redirected to salesforce login page. Enter your credentials, click login, and you will next be prompted to give your app permission to access your salesforce data. After clicking “Allow”, salesforce will then redirect you back to your callback url, which we set up to be the callback action in our salesforce controller. Salesforce will have sent an authorization code in the response parameters.</p>

<p>Now, in your callback action, you can use that authorization code to obtain an access_token:</p>

<script src="https://gist.github.com/vananreed/466e82bae32f71f025c38db9a0c5eaf4.js"></script>

<p>Once you have the access token, you are officially linked to salesforce. You will use this token to make requests and fetch your data from your salesforce account. It is up to you where you want to save it, but I would recommend either save it in your rails application cookies or in a SalesforceAccount Model in your database.</p>

<h3>Need Help?</h3>

<p>Need some help integrating with salesforce in your ruby on rails application? We would love to hear from you and have the chance to use our expertise crafting quality web applications for your product!</p>]]></content><author><name>Reed Van Anrooy</name></author><category term="ruby-on-rails" /><category term="programming" /><category term="development" /><summary type="html"><![CDATA[Salesforce is one of the most widely used Customer Relationship Management (CRM) tools on the web today, and collects valuable data-driven insights for many businesses as they strive for growth. When building tailored software solutions for your business, it’s often a good idea to pull that valuable data you’ve worked so hard to obtain into your application to generate increasingly valuable data tooling.]]></summary></entry><entry><title type="html">What we learned integrating Heartland Payments into the OnTrac Point of Sale: 3 months later</title><link href="https://www.madebylotus.com/blog/what-we-learned-integrating-heartland-payments-into-the-ontrac-point-of-sale-3-months-later/" rel="alternate" type="text/html" title="What we learned integrating Heartland Payments into the OnTrac Point of Sale: 3 months later" /><published>2019-04-15T00:00:00+00:00</published><updated>2019-04-15T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/what-we-learned-integrating-heartland-payments-into-the-ontrac-point-of-sale-3-months-later</id><content type="html" xml:base="https://www.madebylotus.com/blog/what-we-learned-integrating-heartland-payments-into-the-ontrac-point-of-sale-3-months-later/"><![CDATA[<p>There are few things more integral to running a successful business when creating software than transacting money: it's a big deal!</p>

<p>So when we originally launched the OnTrac Point of Sale System for KARM Thrift Stores and other thrift stores throughout the US, we knew we would be circling back around to adding integrated payments via Heartland Payments soon!</p>

<h2>Payment Processing Options</h2>

<p>We typically advocate for using Stripe when integrating payment processing for mobile or web applications; it's streamlined, competitively priced, and very powerful allowing us to build a robust and full featured platform in less time.</p>

<p>However, until very recently Stripe hasn't offered any first-class physical payment processing solutions, such as what we needed to process credit and gift cards in stores connected to our iOS app. With an established relationship with Heartland Payments already in place at KARM Thrift Stores, the decision to integrate using their network and hardware offerings was simple for our client.</p>

<h2>It's all about the Hardware</h2>

<p>Heartland Payments is a payment processor, but not a hardware manufacturer. Therefore, they had a list of supported units from manufactures that we could use in our integration, and an SDK for communicating with those approved hardware units.</p>

<p>Our experience integrating their Objective-C SDK into our Swift iOS app was not the most pleasant from a developer perspective. The SDK is large, inconsistent, pieces are very poorly written and we encountered several bugs that took a lot of time to troubleshoot with the Heartland integration team via email.</p>

<p><a href="/portfolio/on-trac/"><img src="/static/images/posts/what-we-learned-integrating-heartland-payments-into-the-ontrac-point-of-sale-3-months-later/ontrac-pos.46d18e1df4.webp" class="img-responsive" width="650" height="434" alt="The OnTrac POS payment screen on an iPad"></a></p>

<p><a href="/portfolio/on-trac/">Read more on how we offered integrated payments for OnTrac</a></p>

<h2>Low level integration</h2>

<p>As part of our integration, we were informed that the connection between our iOS app and the hardware terminal itself was sometimes <em><strong>expected</strong></em> to fail. I could repeat for importance.</p>

<p>This meant that the SDK we were using to send a command to the hardware card terminal, would sometimes just not execute a callback to our main application and we'd be lost waiting on a response from the terminal that would never come.</p>

<p>The recommended solution was to create a mechanism where we would request the details of the most recently completed transaction from the card terminal, after a certain timeout and having not received a response from the card terminal. The Heartland SDK didn't implement this functionality, so we had to enter into a Non-Disclosure Agreement to be a 3rd party vendor and gain access to the low-level spec for communicating with the card terminal itself.</p>

<p>Armed with a several hundred page low-level spec communication document, we set about adding functionality to communicate and request this needed information from the card terminal directly simple to see if the most recent transaction completed was indeed the one we wanted the card terminal to process, and therefore deal with the fact that we never received a callback from the card terminal, even though the transaction went through and the customer and cashier both thought everything worked well.</p>

<p>The solution looks seamless, but cost us a large amount of time debugging, reviewing documentation, troubleshooting the Heartland SDK, writing low-level socket connections in a custom protocol, and so much more.</p>

<h2>Well worth the effort</h2>

<p>Even though the integration was a painful process with many unexpected hiccups due to poor documentation, a poor SDK, lots of bureaucratic tape and overall a complex system of parts, it has been worth the effort in every way.</p>

<p>Our cashiers simply tap a payment method to capture an electronic payment, never leaving our POS app. Customers run their cards on the card terminal themselves which they love. We have less errors, avoid the hefty reconciliation process at the end of the day, have real time transaction info, and all of our systems are integrated with our payment provider so we gain insight into more operations data.</p>

<p>At the end of the day, we're delighted to have made our cashier's role simpler, less error prone, and more enjoyable so they can focus on providing the best customer experience for guests.</p>]]></content><author><name>Matt</name></author><category term="development" /><category term="business" /><summary type="html"><![CDATA[There are few things more integral to running a successful business when creating software than transacting money: it's a big deal!]]></summary></entry><entry><title type="html">Product Discovery: Investing in Your Business</title><link href="https://www.madebylotus.com/blog/product-discovery-investing-in-your-business/" rel="alternate" type="text/html" title="Product Discovery: Investing in Your Business" /><published>2019-04-10T00:00:00+00:00</published><updated>2019-04-10T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/product-discovery-investing-in-your-business</id><content type="html" xml:base="https://www.madebylotus.com/blog/product-discovery-investing-in-your-business/"><![CDATA[<p>You might be under the impression that the most important thing to do when starting your new business and app idea is to get started creating it, but you'd be wrong! Taking the time to sit with an industry expert to explore your business idea, target audience, market, problem and solution is key to your success.</p>

<p><img src="/static/images/posts/product-discovery-investing-in-your-business/product-discovery-investing-in-your-business.89fb1fbf5d.webp" class="img-responsive" width="600" height="900" alt="Product Discovery: Investing in Your Business"></p>

<h2>Start at the Beginning</h2>

<p>It sounds simple, but isn't the beginning a very good place to start? Taking time to review <em>why there is a need for your product</em>, who it's for, what you're solving, heck, how you're even going to make money - these are all super important pieces of info to keep in mind and know before diving headfirst into software development.</p>

<p>We advocate for a discovery process with every customer, especially new business owners or first time app creators. Reasoning being twofold: we get to learn in depth about how you got here and where you're going, allowing us to frame all of our recommendations and effort into your focus; additionally, you get the benefit of further vetting your business model with an industry expert, gaining guidance, perspective, and hopefully critical direction.</p>

<h2>What is this Discovery Process?</h2>

<p>Simply put, the discovery process is a series of sessions with our founder (Matt) working through a process honed with many clients to probe and elicit as much information and clarity as possible before ever embarking on the software creation journey.</p>

<p>Sessions can be on the phone, video conference, or in person; they are structured over usually 4-5 two hour meetings. Typically there are "homework" items for you to research and clarify between sessions and we'll all learn a lot through the process.</p>

<p>At the end, you're given a written prepared document outlining many details about your business requirements, customer base, monetization strategies, functionality and so much more.</p>

<h2>Why would I do all that?</h2>

<p>There is immense value in simply being forced to think and <em>decide</em> certain key factors throughout the early stages of your app creation. Should you offer free with paid in-app upgrades, or capture payment up front at install? Should you target iOS only, or Android as well? The list goes on and on.</p>

<p>By making decisions guided by an industry expert, you've put a lot of groundwork into your business. We've had some clients realize that perhaps the reason they haven't seen this done before is because it can't be profitable and table the idea. We've had other clients rethink their strategy entirely. And we've had clients that gained perspective, but were <strong>doubly</strong> sure of their product and market fit at the end of the process.</p>

<p>No matter the outcome, it's valuable to you and your new venture. On top of that, you'll be so well prepped on what you're wanting to accomplish and how you're going to do it, that getting high-quality, accurate proposals from software vendors will be so much easier to obtain and compare (<a href="/blog/5-tips-for-comparing-software-development-estimates/">hey, we've written about how to compare those proposals here</a>). We hope you'll work with us!</p>]]></content><author><name>Matt</name></author><category term="entrepreneurship" /><category term="business" /><summary type="html"><![CDATA[You might be under the impression that the most important thing to do when starting your new business and app idea is to get started creating it, but you'd be wrong! Taking the time to sit with an industry expert to explore your business idea, target audience, market, problem and solution is key to your success.]]></summary></entry><entry><title type="html">Curious What Goes into Maintaining a Mobile App after Launch?</title><link href="https://www.madebylotus.com/blog/curious-what-goes-into-maintaining-a-mobile-app-after-launch/" rel="alternate" type="text/html" title="Curious What Goes into Maintaining a Mobile App after Launch?" /><published>2019-04-04T00:00:00+00:00</published><updated>2019-04-04T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/curious-what-goes-into-maintaining-a-mobile-app-after-launch</id><content type="html" xml:base="https://www.madebylotus.com/blog/curious-what-goes-into-maintaining-a-mobile-app-after-launch/"><![CDATA[<p>If you're considering founding your own startup or creating your first app, you'll likely be focused on all the important near-term stressors: market validation, user experience, design, development, and so so much more.</p>

<p>However, that's not the end of the journey. In fact, it's just the beginning! While each product and platform will be unique, there are a set of common maintenance tasks that as a new software <em>keeper</em>, you should be aware.</p>

<h2>Annual Fees</h2>

<p>Simply put, you'll owe Apple and Google annual dues for them distributing your published mobile app on their stores. You'll be paying this when you first setup your account, and each year after, even if you had zero in sales (hopefully not!).</p>

<h2>Operating System Releases</h2>

<p>Both Apple and Google release new operating systems on a mostly annual basis; you might have heard of iOS 12 or Android Q for instance.</p>

<p>While you don't have to immediately update your app to run on the new versions of software that the companies will be making available to customer devices, it is good to plan on handling updates.</p>

<p>We generally advise to plan on handling any changes required in the codebase roughly 4-6 months after the launch of a new OS from Apple or Google. Most of the time this isn't a major development cost, but letting these pile up over the years will cause you to eventually be unable to submit to the App Stores at all, be removed from the store, and cost significantly more to perform the upgrade than if you had spread it out and done the work each year.</p>

<h2>New Features</h2>

<p>You'll no doubt have a list of features at launch that didn't make it into the initial release, and hopefully be tracking customer feedback to prepare a healthy product backlog (<a href="/blog/3-tips-for-quickly-prioritizing-your-product-backlog/">read more about maintaining your backlog here</a>), but you still need to expect the unexpected feature to suddenly become important.</p>

<p>We see that most often we'll get a product to market and immediately get feedback that is amazing (hey, that's the point of deliver early and often) for a new feature. It's just the nature of gaining more exposure from users. Plan accordingly.</p>

<h2>Routine Maintenance</h2>

<p>Even with planned features, annual upgrades, and customer support, you should consider your product codebase a living, breathing thing.</p>

<p><strong><em>Think of your app codebase as a puppy. Feed her, love her, spend time with her; she's a happy, healthy puppy. If you neglect your codebase, you'll have a cranky puppy that's harder to train and manage.</em></strong></p>

<p><img src="/static/images/posts/curious-what-goes-into-maintaining-a-mobile-app-after-launch/grumpy-puppy.e856b5467b.webp" class="img-responsive" width="600" height="638" alt="A grumpy-looking puppy sitting in the grass"></p>

<p>Some technical solutions require more routine maintenance than others. For native mobile applications written in Apple's Swift language or Google's Kotlin, there are updates needed but planning on once a quarter is probably safe.</p>

<p>Looking to make use of the latest and greatest powerful features by deploying a native mobile application written in React Native (a single codebase supporting both Android and Apple devices)? You're going to want a developer maintaining the codebase at least monthly to account for the rapid change in the technology.</p>

<h2>Curious for More?</h2>

<p>Still have more questions? We'd love to help!</p>

<p>We not only offer our leadership in determining the best ways to craft an app, but we're experienced in supporting production apps and want you to succeed. We'd love to bring that experience to your team today.</p>]]></content><author><name>Matt</name></author><category term="entrepreneurship" /><summary type="html"><![CDATA[If you're considering founding your own startup or creating your first app, you'll likely be focused on all the important near-term stressors: market validation, user experience, design, development, and so so much more.]]></summary></entry><entry><title type="html">Learn how we utilize our experience crafting mobile apps to rebrand a focused travel app experience in record time</title><link href="https://www.madebylotus.com/blog/were-excited-to-announce-guzo/" rel="alternate" type="text/html" title="Learn how we utilize our experience crafting mobile apps to rebrand a focused travel app experience in record time" /><published>2019-03-20T00:00:00+00:00</published><updated>2019-03-20T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/were-excited-to-announce-guzo</id><content type="html" xml:base="https://www.madebylotus.com/blog/were-excited-to-announce-guzo/"><![CDATA[<p>We feel like a broken record: we really love working with all of our clients and seeing their products come to life before our eyes!</p>

<p>Guzo is no exception. We have spent a <strong>lot</strong> of time collaborating with the awesome team behind Guzo (formerly Croozen) and we're so happy to see them officially launch with a rebrand and focus on delivering truly stellar travel experiences.</p>

<blockquote><em>Guzo is a collaborative travel network where people can build trips and share them with others.</em></blockquote>

<p><img width="1360" height="907" src="/static/images/posts/were-excited-to-announce-guzo/guzo-teaser.644092f072.webp" class="img-responsive" alt="Illustration of two friends driving a convertible, with the Guzo logo"></p>

<h2>Curious to Learn More?</h2>

<p>Want to know how we put our full team behind the Guzo product and pulled off a master rebrand, overhaul and product platform launch in record time?</p>

<p><a href="/portfolio/guzo/">We have our Case Study on Guzo Right Here.</a></p>

<p><img width="1360" height="1020" src="/static/images/posts/were-excited-to-announce-guzo/guzo-in-car.a8d28ce0cb.webp" class="img-responsive" alt="The Guzo app on a phone held in a car"></p>]]></content><author><name>Matt</name></author><category term="business" /><summary type="html"><![CDATA[We feel like a broken record: we really love working with all of our clients and seeing their products come to life before our eyes!]]></summary></entry><entry><title type="html">5 Tips for Comparing Software Development Estimates</title><link href="https://www.madebylotus.com/blog/5-tips-for-comparing-software-development-estimates/" rel="alternate" type="text/html" title="5 Tips for Comparing Software Development Estimates" /><published>2019-03-15T00:00:00+00:00</published><updated>2019-03-15T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/5-tips-for-comparing-software-development-estimates</id><content type="html" xml:base="https://www.madebylotus.com/blog/5-tips-for-comparing-software-development-estimates/"><![CDATA[<p>If you're in the market for a software agency, you have most likely encountered many different approaches for the proposal and estimation process. Each approach has advantages for both you or the agency, but we'll discuss those in a separate post.</p>

<p>For today, let's talk about 5 things you should be doing to ensure that you are comparing estimates from different agencies in the best manner possible.</p>

<h2>Tip 1: Don't Base on Price Alone</h2>

<p>This should be obvious, but simply choosing the estimate with the lowest estimated cost doesn't mean that you'll get the best product, or that you'll even pay that estimated amount (more on that later). Likewise, just because the estimated cost is the highest doesn't mean you'll necessarily get the best product; however, I'd argue that your chances are better.</p>

<p>We talk about how pricing affects quality and <a href="/blog/the-hidden-cost-of-a-cheaper-hourly-rate/">why cheaper isn't always better</a> a few times on this blog.</p>

<h2>Tip 2: Reputation &amp; Evaluation</h2>

<p>Have you worked with any of these entities previously; if so, your inside knowledge could say you easily. If they are all new to you, you should take the time to seek out online reviews, ask to speak with previous customers for references, etc.</p>

<p>Approach the relationship almost as a new-hire for your business; you wouldn't hire someone off the street without some leg work, so do the same for your new software craftspeople.</p>

<p>Hint: We have a <a href="/blog/what-youre-not-asking-your-development-partner/">guide to what you should be asking your potential software development partners</a> here for you to download and explore in detail.</p>

<h2>Tip 3: Fee Structure</h2>

<p>Let's say that you have two estimates with the exact same dollar amount; which is better?</p>

<p>We can't offer legal advice, but in general there are a few items to take note of when it comes to pricing structures for custom software development.</p>

<p><strong>Time and Materials</strong>: The developer has estimated the project at an amount, but you are agreeing to pay the actual amount for the project upon completion. This means you may save some money, but leaves you liable for potentially a huge unplanned cost and is hard to budget. Often in this agreement with our clients, we find the flexibility is liberating for long-term relationships and constant development, but prioritization of features and ROI is key to success.</p>

<p><strong>Fixed Price</strong>: The developer is agreeing to offer the completed product to you at a fixed price; no matter the amount of effort required. You should see this as a <em>value proposition</em>. Will the resulting product produce value for my business greater than the cost to produce? If so, you've met your investment and the agreement may be valuable. You have no liability for overages on the product, but you may find yourself butting heads with a developer wanting to nickel-and-dime you.</p>

<p>Whatever way you choose, you'll want to pay attention to fee schedules, expenses, timelines, milestones, invoice terms, payment arrangements, late fees, arbitration and so much more.</p>

<h2>Tip 4: Negotiate and Communicate</h2>

<p>Hopefully you've subscribed to our methodology of discovery before implementation, but on any proposal discussion and negotiation is completely acceptable.</p>

<p>Find that you really want to work with option #1, but yet the price is just too high or timeline too far out? <em>Have a conversation. </em>We're all humans, perhaps something was miscommunicated, or personnel changes could be made to better meet your timeline, or there are features/options to change in the pricing structure to better meet your targeted budget.</p>

<p>Just know that if you feel like you want to work with that software craftsperson, have a dialog and find a way. Truly good creators will want to work on your project and take the journey with you and usually make reasonable effort to find the best solution for you.</p>

<h2>Tip 5: Don't Think too Long</h2>

<p>Just know that for every week (or even day!) you spend comparing estimates, contemplating scope, and generally making no active progress forward, your competitors may be!</p>

<p>If you have a proposal that seems fair, is detailed, meets your relative budget and you have a good feeling about engaging with the software creators, then make the move! You can always pivot and adjust (hey, that's called being agile), but if you never start, you'll never finish the race.</p>]]></content><author><name>Matt</name></author><category term="entrepreneurship" /><category term="business" /><summary type="html"><![CDATA[If you're in the market for a software agency, you have most likely encountered many different approaches for the proposal and estimation process. Each approach has advantages for both you or the agency, but we'll discuss those in a separate post.]]></summary></entry><entry><title type="html">What it really means for you to make the jump and freelance full time</title><link href="https://www.madebylotus.com/blog/what-it-really-means-for-you-to-make-the-jump-and-freelance-full-time/" rel="alternate" type="text/html" title="What it really means for you to make the jump and freelance full time" /><published>2018-12-05T00:00:00+00:00</published><updated>2018-12-05T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/what-it-really-means-for-you-to-make-the-jump-and-freelance-full-time</id><content type="html" xml:base="https://www.madebylotus.com/blog/what-it-really-means-for-you-to-make-the-jump-and-freelance-full-time/"><![CDATA[<p>Lately I've received a few questions from new acquaintances, simply asking for some advice on how they could take the plunge to freelance full time and what that means for them and their families.</p>

<p>Where there is one, there are many, so I thought I'd put together a list of things you should prepare for when you begin freelancing full time.</p>

<h2>You will be stressed</h2>

<p>There is no way around it; changes are, you're already stressed just thinking about it. In some ways, there is a euphoria from just taking action and starting down the long path of making your freelancing dream come true.</p>

<p>Just know that no matter how much you work, plan and organize, there will always be a nagging stress and worry when you freelance full time.</p>

<h2>You are the boss</h2>

<p>This is both good and bad of course. You can take assurance in the fact that you are fully in charge; there is no one to stand in your way and impede progress or change. If you're unhappy with how something is going, you have the power to enact change, perhaps even instantly.</p>

<p>The drawback of course is a lack of mentorship, learning as you go, mistakes, and finding the time to research and fix a problem.</p>

<h2>You are now a salesperson</h2>

<p>You need to learn this as quickly as you can; you are a salesperson and should always be selling. At your mechanic and thinking how you were just on their website and couldn't figure out how to call them easily? Suggest your services while you're there.</p>

<p>Every new connection, every opportunity is yours for the taking. You needn't stand on top of the table at your family reunion and rant about the merits of the latest development in your industry, but you need to keep alert to potential sales opportunity.</p>

<h2>Life can be lonely</h2>

<p>Take a moment to realize that life as a freelancer can be lonely. When patching together work from multiple clients on deadlines, it can be tempting to grind away producing as quickly as you can, without much interaction with colleagues.</p>

<p>Finding time to connect in meaningful ways with others in your community, be it online or in person, can go a long way to making your work day more enjoyable. You'll have the added benefit of sharing client horror stories, tips and may even find yourself able to give advice to a new freelancer sooner than you think!</p>]]></content><author><name>Matt</name></author><category term="entrepreneurship" /><summary type="html"><![CDATA[Lately I've received a few questions from new acquaintances, simply asking for some advice on how they could take the plunge to freelance full time and what that means for them and their families.]]></summary></entry><entry><title type="html">Looking Back at 10 Years as a Software Developer</title><link href="https://www.madebylotus.com/blog/looking-back-at-10-years-as-a-software-developer/" rel="alternate" type="text/html" title="Looking Back at 10 Years as a Software Developer" /><published>2018-11-14T00:00:00+00:00</published><updated>2018-11-14T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/looking-back-at-10-years-as-a-software-developer</id><content type="html" xml:base="https://www.madebylotus.com/blog/looking-back-at-10-years-as-a-software-developer/"><![CDATA[<p>Wow, <em>10 years</em>!</p>

<p>It hit me recently that I've been working full time as a professional software developer for 10 years.</p>

<p>There have been ups and downs along the way and given more thought and feedback to the #100DaysOfCoding movement, I thought providing an overview and mini-series of what I've learned could help others just beginning their journey.</p>

<h2>10 Years of Software Teams, Projects and Code</h2>

<p>To understand what I've learned, you need to know where I've been.</p>

<p><img src="/static/images/posts/looking-back-at-10-years-as-a-software-developer/10-years-as-developer.1151f81f5f.webp" class="img-responsive" width="600" height="600" alt="Looking Back: 10 Years as a Software Engineer"></p>

<h3>The Early Years - Consultancy</h3>

<p>I was working towards a BS in Computer Science focused on systems programming (mainly C and C++) when I was hired at a local consultancy to build, of all things, windows installer packages. Yes, you read that right. During the next few years, I build installers for windows, installers for Mac, learned web development in PHP, wrote Mac apps in Objective-C, and launched my first iOS app in the App Store.</p>

<p>Being part of a small business of roughly 20, I met with clients, learned the importance of sales and pricing projects correctly, and so much about operations.</p>

<p><em>I didn't realize it at the time, but this was an amazing way to start my career and understand so much about business as a whole.</em></p>

<h3>The Middle Years - Product Teams</h3>

<p>After pouring my heart into consultancy work for years and getting burnt out on the endless cycle of clients, I pursued product work. During this time I worked with NASCAR building internal systems to streamline and digitize processes, worked with an amazing internally distributed remote team to build complex software for clinical drug trial management, built HR software and so much more.</p>

<p>I learned what it meant to work remote, the importance of aligning a team around a single goal, how things could fall apart when a remote team wasn't run well, and much more.</p>

<p><em>Most importantly, I worked with incredibly talented, professional and organized management and technical people and developed an immense appreciation for more formal software development processes: automated testing, backlog grooming, discovery processes, story writing and client demos.</em></p>

<p>I enjoyed working on product teams, but did miss some of the challenge of balancing multiple projects or learning new technologies that consultancy work provided.</p>

<p><img src="/static/images/posts/looking-back-at-10-years-as-a-software-developer/10-year-software-developer-journey.100426a6fd.webp" class="img-responsive" width="600" height="338" alt="Animation of a desk with a laptop, coffee, phone and notebooks: Share the Journey, 10 Years of Software"></p>

<h3>Recent Past - Blend the Two</h3>

<p>The opportunity to start Lotus and work full time for myself presented itself (more on that decision later) and I left product teams behind. I was now in a position to blend the two work styles in such a way that makes me personally happy, and is actually great for business.</p>

<p>These days I have a great selection of long term clients where we act as the product team, managing a backlog, implementing features and providing support, year after year. On top of that, we're constantly looking for new clients to build an MVP or an entire product platform, much as you might find a consultancy do.</p>

<p><em>The key difference that our customers and I love - we don't walk away when we launch a product. In fact, we do everything we can to support and encourage growth so we can be part of that journey.</em></p>

<p>It just makes sense.</p>

<h2>In Depth - Lessons Learned</h2>

<p>This will kick off a series of articles and potentially an ebook detailing specific lessons learned throughout my career and things I can share to help other starting developers.</p>

<p>I look forward to sharing my journey with you!</p>]]></content><author><name>Matt</name></author><category term="entrepreneurship" /><category term="business" /><summary type="html"><![CDATA[Wow, 10 years!]]></summary></entry><entry><title type="html">Utilizing the Power of ElasticSearch and PostgreSQL with ZomboDB</title><link href="https://www.madebylotus.com/blog/utilizing-the-power-of-elasticsearch-and-postgresql-with-zombodb/" rel="alternate" type="text/html" title="Utilizing the Power of ElasticSearch and PostgreSQL with ZomboDB" /><published>2018-10-10T00:00:00+00:00</published><updated>2018-10-10T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/utilizing-the-power-of-elasticsearch-and-postgresql-with-zombodb</id><content type="html" xml:base="https://www.madebylotus.com/blog/utilizing-the-power-of-elasticsearch-and-postgresql-with-zombodb/"><![CDATA[<p>Looking to add enhanced search capabilities to your Postgres backed project and unsure of where to turn?</p>

<p><em>ElasticSearch and ZomboDB will make things a lot easier!</em></p>

<p><img width="1024" height="512" src="/static/images/posts/utilizing-the-power-of-elasticsearch-and-postgresql-with-zombodb/zombodb-marriage.9d5021cba4.webp" class="img-responsive" alt="PostgreSQL marries ElasticSearch"></p>

<p>ZomboDB exposes a query syntax and native Postgres indexes in your existing Postgres installation, allowing you to used an ElasticSearch backed index by querying directly in Postgres.</p>

<p><strong>Let that sink in. </strong></p>

<p>Query data, insert, update - all via Postgres and no worries about keeping things in sync with your ElasticSearch engine.</p>

<blockquote>Having everything map through Postgres as a single source of truth is powerful. It is up to ZomboDB to make sure that the data ends up in ElasticSearch in one piece and you don’t have to worry about it.</blockquote>

<p>https://sanderf.nl/misc-why-zombodb-is-neat.html</p>

<p>We enjoyed working with the creator of ZomboDB a few years back on the actual www.zombodb.com website - Eric is pretty laid back and a great maintainer and consultant for ZomboDB.</p>

<p>Next time you're considering a full stack powerful search solution for your Postgres solution, check out ZomboDB.</p>]]></content><author><name>Matt</name></author><category term="programming" /><category term="development" /><summary type="html"><![CDATA[Looking to add enhanced search capabilities to your Postgres backed project and unsure of where to turn?]]></summary></entry><entry><title type="html">Remembering Matthew Shepard: 20 Years Later</title><link href="https://www.madebylotus.com/blog/remembering-matthew-shepard-20-years-later/" rel="alternate" type="text/html" title="Remembering Matthew Shepard: 20 Years Later" /><published>2018-10-05T00:00:00+00:00</published><updated>2018-10-05T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/remembering-matthew-shepard-20-years-later</id><content type="html" xml:base="https://www.madebylotus.com/blog/remembering-matthew-shepard-20-years-later/"><![CDATA[<p>We're taking a break from our normal technical and small business oriented content today to look back 20 years on an event defining modern history for LGBTQ youth in America.</p>

<blockquote>We cannot appreciate today, nor guide our future, without first understanding our past.</blockquote>

<p><img src="/static/images/posts/remembering-matthew-shepard-20-years-later/matthew-shepard.b50b29cd6d.webp" class="img-responsive" width="600" height="389" alt="Black and white portrait of Matthew Shepard"></p>

<h2>The Importance</h2>

<p>For many, Matthew Shepard's brutal beating and subsequent death in Wyoming on October 6th, 1998, was a wake up call to the underlying discrimination, hatred and violence LGBTQ peoples in America face daily. It brought national attention, changes in law, culture, and awareness.</p>

<p><em>A young man had to be beaten, left on the roadside, and died for that attention.</em></p>

<h2>Where We Are Today in 2018</h2>

<p>Even with that attention and 20 years behind us, we still have so many things challenging LGBTQ peoples in America alone. Yes, we can marry; but we lack basic protections for being fired from our jobs, denied housing, denied or unfairly questioned to donate blood, prevented from adopting children or fostering, or lack protection under hate crime laws.</p>

<blockquote>Five states – including Wyoming – still don’t have criminal hate crime laws. In another 14, hate crime laws don't protect individuals for their sexual orientation. And still in 27 states, you can legally be fired from your job for being gay.</blockquote>

<p>https://www.usatoday.com/</p>

<p>Take pause and thanks for the progress we have made, in part to Matthew Shepard 20 years ago, but there is so much more to do.</p>

<h2>Take Action</h2>

<p>Take this information and put your feelings to use: mark your calendar to vote in the upcoming midterm elections and donate to the Human Rights Campaign.</p>

<p><a href="http://give.hrc.org/page/21062/donate/1/">Support the HRC in the fight for Equality</a></p>]]></content><author><name>Matt</name></author><summary type="html"><![CDATA[We're taking a break from our normal technical and small business oriented content today to look back 20 years on an event defining modern history for LGBTQ youth in America.]]></summary></entry><entry><title type="html">A Year Working with Croozen to Launch a New Cloud Platform</title><link href="https://www.madebylotus.com/blog/a-year-working-with-croozen-to-launch-a-new-cloud-platform/" rel="alternate" type="text/html" title="A Year Working with Croozen to Launch a New Cloud Platform" /><published>2018-10-04T00:00:00+00:00</published><updated>2018-10-04T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/a-year-working-with-croozen-to-launch-a-new-cloud-platform</id><content type="html" xml:base="https://www.madebylotus.com/blog/a-year-working-with-croozen-to-launch-a-new-cloud-platform/"><![CDATA[<p>This announcement is a long time in the making, mainly because we've been so busy and it's hard to find time to share yet another awesome product launch from the team!</p>

<p><img width="1024" height="512" src="/static/images/posts/a-year-working-with-croozen-to-launch-a-new-cloud-platform/gordon-taylor-croozen-quote.68273bd5df.webp" class="img-responsive" alt="“The communication and timeliness of the team is definitely the best experience we have had.” Gordon Taylor"></p>

<h2>Croozen Launch</h2>

<p>We began work with the team at Croozen in late 2017, going through our discovery process uncovering what their needs would be to begin a platform rewrite at the start of 2018.</p>

<p>Croozen was stuck with a crumbling and poorly designed Parse-based API that was holding them back. We worked to completely approach the platform with a fresh set of eyes, using discovery and architecture design sessions to ensure we built a product that would scale and allow quick iteration.</p>

<p>We launched the new platform in April 2018 and have been moving at breakneck speed ever since!</p>

<h2>Croozen Now</h2>

<p>This year has seen Croozen explode in popularity, features, platform usage and community partnerships. We've enjoyed being part of that journey to provide advisement, project and product management, feature development and maintenance as the team continues to iterate and deliver.</p>

<p>We're excited to see us roll out some major changes and improvements (post coming later) to wrap up 2018 and poise us to kill 2019!</p>

<h2>Our Case Study</h2>

<p>For more details on how we've worked with Croozen to lay a solid foundation for the product platform and experience growth (hint: we can do that for your startup too) read our case study.</p>

<p><a href="/portfolio/croozen/">Read the Croozen Case Study</a></p>]]></content><author><name>Matt</name></author><category term="business" /><summary type="html"><![CDATA[This announcement is a long time in the making, mainly because we've been so busy and it's hard to find time to share yet another awesome product launch from the team!]]></summary></entry><entry><title type="html">Why Ads are Frustrating on the Apple App Store</title><link href="https://www.madebylotus.com/blog/why-ads-are-frustrating-on-the-apple-app-store/" rel="alternate" type="text/html" title="Why Ads are Frustrating on the Apple App Store" /><published>2018-10-01T00:00:00+00:00</published><updated>2018-10-01T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/why-ads-are-frustrating-on-the-apple-app-store</id><content type="html" xml:base="https://www.madebylotus.com/blog/why-ads-are-frustrating-on-the-apple-app-store/"><![CDATA[<p>Recently we noticed that searching for an app by name seemed to reliably show us an ad at the top of the page for a competitor. Paying even closer attention, we were able to reproduce this several times.</p>

<p>Talk about jarring - just look how often you can search for an app by name and the first result is obscured by an ad for a competing product!</p>

<p><img width="1360" height="1020" src="/static/images/posts/why-ads-are-frustrating-on-the-apple-app-store/app-store-ads.f7fc73d037.webp" class="img-responsive" alt="Three iPhones showing App Store searches where an ad for a competing app appears above the app searched for"></p>

<p>Take a look at these examples above; searching for "Lyft" shows me Uber first, and searching for "Uber" shows me Lyft first. Talk about maddening!</p>

<p>These are examples of companies with many ad dollars to spend, but I'm just as confused why searching for "Uber" wouldn't show an ad for Uber at the top? Seems a bit misleading to users, and hurtful to app developers as well.</p>

<p>What are your thoughts?</p>]]></content><author><name>Matt</name></author><category term="marketing" /><summary type="html"><![CDATA[Recently we noticed that searching for an app by name seemed to reliably show us an ad at the top of the page for a competitor. Paying even closer attention, we were able to reproduce this several times.]]></summary></entry><entry><title type="html">Stripe Raises Another $245M and Can Work for Your Small Business</title><link href="https://www.madebylotus.com/blog/stripe-raises-another-245m-and-can-work-for-your-small-business/" rel="alternate" type="text/html" title="Stripe Raises Another $245M and Can Work for Your Small Business" /><published>2018-09-27T00:00:00+00:00</published><updated>2018-09-27T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/stripe-raises-another-245m-and-can-work-for-your-small-business</id><content type="html" xml:base="https://www.madebylotus.com/blog/stripe-raises-another-245m-and-can-work-for-your-small-business/"><![CDATA[<p>Stripe, the payments processing startup offering streamlined integration services for business internationally, has raised an additional $245M in funding led by Tiger Global Management and other firms. Just two years after a $9B valuation, powerhouse Stripe is now valued at over $20B.</p>

<p><img width="1024" height="512" src="/static/images/posts/stripe-raises-another-245m-and-can-work-for-your-small-business/stripe.19d169fe18.webp" class="img-responsive" alt="Stripe logo"></p>

<h2>Powering Your Small Business</h2>

<p>What's impressive is the thought of putting that additional funding to work for your small business. Stripe continues to innovate and empower small businesses with enterprise grade payment processing and fraud protection tools at a scalable &amp; affordable price.</p>

<p>If you haven't yet used Stripe to provide transactions, recurring subscriptions, coupons or complex invoice á la cart pricing for your e-commerce or SaaS platform, now may be the time.</p>

<p>Read more about <a href="https://techcrunch.com/2018/09/26/stripe-is-now-valued-at-20b-after-raising-another-245m-led-by-tiger-global/">Stripe's recent valuation</a> on Tech Crunch.</p>

<h2>We're Stripe Experts</h2>

<p>We build complex Stripe integrations utilizing powerful features every day for our customers. If you're thinking of implementing a "Uber" style model holding funds to pay vendors later, collect platform fees, double-entry accounting, or more, chances are we've built it.</p>

<p>We love utilizing the power of the Stripe platform to build solutions for our customers and would love to put that knowledge to use for you.</p>]]></content><author><name>Matt</name></author><category term="business" /><summary type="html"><![CDATA[Stripe, the payments processing startup offering streamlined integration services for business internationally, has raised an additional $245M in funding led by Tiger Global Management and other firms. Just two years after a $9B valuation, powerhouse Stripe is now valued at over $20B.]]></summary></entry><entry><title type="html">3 Tips to Craft More Effective Email Subjects</title><link href="https://www.madebylotus.com/blog/3-tips-to-craft-more-effective-email-subjects/" rel="alternate" type="text/html" title="3 Tips to Craft More Effective Email Subjects" /><published>2018-09-18T00:00:00+00:00</published><updated>2018-09-18T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/3-tips-to-craft-more-effective-email-subjects</id><content type="html" xml:base="https://www.madebylotus.com/blog/3-tips-to-craft-more-effective-email-subjects/"><![CDATA[<p>An email subject is your first impression and opportunity to grab a prospect's attention and hopefully get them into your email. From there you have to convince, sell, and call to action to drive back to your product.</p>

<p>So, if the email subject is the start of it all and we're all bombarded with emails daily, how can you easily improve your open rates and give yourself more chances to sell your product?</p>

<h3>1. Short and Sweet</h3>

<p>Most of us have limited amounts of time and find ourselves skimming content online like never before. You really only have a split second to make your email stand out and grab the attention of the receiver before you are lost in an inbox or deleted entirely.</p>

<p>Keeping the subject short and direct helps to convey <em>why</em> the receiver should open that email right then. Being direct will increase your chance of that open.</p>

<h3>2. Personalized and Helpful</h3>

<p>While simply including the person's name for personalization is a nice first step, to really set your email apart from others you should be considering dynamic email subjects that are more pertinent to the receiver.</p>

<p>If your platform uses location information, perhaps include the name of the receiver's city. If you have the name of their company, perhaps use that. If you know that the receiver dropped off while booking on your platform, use that information to draw the user back in and be direct and helpful in how opening this message will allow them to complete a task.</p>

<h3>3. Fear of Missing Out</h3>

<p>It's human nature to not want to miss out on something awesome, and you can capitalize upon that in your email marketing with a few quick tricks.</p>

<p>Try creating a sense of urgency, either with time, number of items left, or simply using words such as "urgent" or "action required".</p>

<h3>Even More</h3>

<p>For even more ideas and tips on crafting personalized and effective email subjects, check out this extensive article from AWeber.<a href="https://blog.aweber.com/email-marketing/your-guide-to-writing-the-worlds-best-email-subject-lines.htm">Your Guide to Writing the World's Best Email Subject Lines - Email Marketing Tips</a></p>]]></content><author><name>Matt</name></author><category term="entrepreneurship" /><category term="business" /><summary type="html"><![CDATA[An email subject is your first impression and opportunity to grab a prospect's attention and hopefully get them into your email. From there you have to convince, sell, and call to action to drive back to your product.]]></summary></entry><entry><title type="html">3 Tips for Quickly Prioritizing Your Product Backlog</title><link href="https://www.madebylotus.com/blog/3-tips-for-quickly-prioritizing-your-product-backlog/" rel="alternate" type="text/html" title="3 Tips for Quickly Prioritizing Your Product Backlog" /><published>2018-09-13T00:00:00+00:00</published><updated>2018-09-13T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/3-tips-for-quickly-prioritizing-your-product-backlog</id><content type="html" xml:base="https://www.madebylotus.com/blog/3-tips-for-quickly-prioritizing-your-product-backlog/"><![CDATA[<p>Having a healthy product backlog is an important task for any team to tackle to help ensure the success of the platform and product over time. However, we find when working with teams it is one of the most overlooked tasks and the first thing teams procrastinate on when time is short.</p>

<p>So how can you keep your product backlog healthy and helpful to the team, without spending hours a week just on "management" or maintenance? We have a few tricks to share!</p>

<h2>1. Recurring Grooming</h2>

<p>We're humans and all pulled in many different directions. If you wait until your team has time to groom (or review) the backlog, chances are you'll be putting it off until it's a nightmarish task to tackle.</p>

<p>Agree on a time that should work well for most people - but not necessarily all. Don't be afraid to have people skip grooming, just ensure there is a healthy attendance to make it worthwhile.</p>

<p>If you find grooming is taking too long, you should be doing it more frequently. We tweak over time, but typically find that for our clients grooming bi-weekly allows us to keep the meeting at 30 minutes or less. There are always days where a product owner brings a lot to the meeting for us to add, but we strive for short and sweet so everyone doesn't dread it.</p>

<p><img width="1080" height="1080" src="/static/images/posts/3-tips-for-quickly-prioritizing-your-product-backlog/tips-product-backlog.22cc507131.webp" class="img-responsive" alt="3 Tips for Prioritizing Your Product Backlog"></p>

<h2>2. Come Prepared</h2>

<p>It sounds simple, but if you have to spend time as a group reviewing backlog stories before you can even groom them, you can easily find yourself spending a substantial amount of time just reading stories. No one enjoys listening to Susan ramble through stories aloud on a video call, sharing her screen, while most are aware of the backlog contents.</p>

<p>Ensure you have 5-10 minutes personally before the grooming meeting to scan through stories. Familiarize yourself with any new ones, make notes to ask questions later, etc. You'll be more prepared, and with everyone taking this time the meetings go much faster and are more productive.</p>

<p><em>Pro tip: If you struggle to find that "pre time" meeting prep, your team can try scheduling the meeting at say 10 after the hour. Mentally most of us think of meetings starting at "even" intervals, so you'll most likely think to prep even 5 minutes before the call just because you're in the mindset of a call at 9am for example.</em></p>

<h2>3. Don't Over Do It</h2>

<p>Having a backlog is only helpful if it's organized and detailed, but don't waste time just for the sake of that backlog. It's ok for the team to be pulled away for an emergency production hotfix, or for a holiday to throw things off; these things happen.</p>

<p>Set a time limit on the meeting and stick to it. Refine and discuss important items, and push lower priority stories farther down in the backlog for later. It's hard to sometimes resist the urge to discuss something low-priority, because those tend to be exciting "pie in the sky" features. Let them dwindle if there isn't enough time.</p>

<h2>Be a Backlog Pro</h2>

<p>Hopefully these 3 tips help you better manage your teams backlog and keep everyone engaged and helpful during grooming sessions and in between. Remember, the goal of having a groomed backlog is to make the team run smoothly and have features ready to go for implementation.</p>

<p>Already a pro and have a helpful tip we didn't think of sharing here? We'd love to hear about it, drop us a line!</p>]]></content><author><name>Matt</name></author><category term="entrepreneurship" /><category term="business" /><summary type="html"><![CDATA[Having a healthy product backlog is an important task for any team to tackle to help ensure the success of the platform and product over time. However, we find when working with teams it is one of the most overlooked tasks and the first thing teams procrastinate on when time is short.]]></summary></entry><entry><title type="html">The Hidden Cost of a Cheaper Hourly Rate</title><link href="https://www.madebylotus.com/blog/the-hidden-cost-of-a-cheaper-hourly-rate/" rel="alternate" type="text/html" title="The Hidden Cost of a Cheaper Hourly Rate" /><published>2018-09-11T00:00:00+00:00</published><updated>2018-09-11T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/the-hidden-cost-of-a-cheaper-hourly-rate</id><content type="html" xml:base="https://www.madebylotus.com/blog/the-hidden-cost-of-a-cheaper-hourly-rate/"><![CDATA[<p>Everyone expects to get <em>more</em> when paying more, but what do you give up when paying less? There are hidden costs to your business that you're ignoring if you're focused on the cheapest hourly rate.</p>

<h3>Lack of Innovation</h3>

<p>Industry trends, best practices, and emerging technologies change fast. By engaging with a partner solely focused on delivering at the cheapest rate, you're stifling innovation and the opportunity to reap those rewards in your business.</p>

<h3>Thought Leadership</h3>

<p>You might not think you need thought leadership, but the fact is you most likely are not a software craftsperson and your expertise lays elsewhere in your business. By focusing your partnership on a cheaper hourly rate, you're missing out on the benefits having leadership and advice from experts in the community can bring to your business.</p>

<h3>Mistakes Happen</h3>

<p>Everyone is human and makes mistakes. However, the simple fact is that most often a direct correlation can be made between hourly rate and experience. More experienced people will have <em>already made those mistakes</em>. Take advantage and reap the benefits of having an experienced craftsperson architect, design and build a feature or product that avoids common pitfalls or mistakes a less experienced person might fall victim to.</p>

<h3>Get it Right the First Time</h3>

<p>We hear far too often, "we should have just went with your original proposal". We don't take any joy from hearing that!</p>

<p>By chasing the cheapest solution, customers find themselves in a situation where the product was poorly written, discovery wasn't adequately performed and there are fundamental user experience falls in the product, or features just don't work.</p>

<p>At that point, you have invested in a subpar solution and don't even have your original budget. It's a hard lesson for any customer.</p>

<h3>Read More</h3>

<p>Interested in learning how else you benefit from working with a premium partner? Just so happens we've written about that too!</p>

<p><a href="/blog/behind-the-scenes-hourly-rate/">Learn how you benefit from a premium rate</a></p>]]></content><author><name>Matt</name></author><category term="entrepreneurship" /><summary type="html"><![CDATA[Everyone expects to get more when paying more, but what do you give up when paying less? There are hidden costs to your business that you're ignoring if you're focused on the cheapest hourly rate.]]></summary></entry><entry><title type="html">Behind the Scenes: Hourly Rate</title><link href="https://www.madebylotus.com/blog/behind-the-scenes-hourly-rate/" rel="alternate" type="text/html" title="Behind the Scenes: Hourly Rate" /><published>2018-09-07T00:00:00+00:00</published><updated>2018-09-07T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/behind-the-scenes-hourly-rate</id><content type="html" xml:base="https://www.madebylotus.com/blog/behind-the-scenes-hourly-rate/"><![CDATA[<p>As software craftspeople, we hear it quite often; how did we determine our hourly rate, or why can't we shave just a little off the cost for a client. It's an important discussion to have with any client, ensuring they see our value and what that rate provides.</p>

<h2>Higher Rates Benefit Us All</h2>

<p>This might sound counterintuitive, but a higher rate actually helps both parties. Not convinced?</p>

<p><a href="/blog/the-hidden-cost-of-a-cheaper-hourly-rate/">Read the hidden costs you're ignoring by paying a cheaper hourly rate.</a></p>

<h3>Skill Indicator</h3>

<p>When as skilled workers, we become more experienced than our more junior peers, we're quicker, better and more adept at accomplishing a task. In order to do more work at a higher quality and avoid <em>less pay</em>, we have to charge more. You can certainly find overpriced examples, but in general the higher the price the higher the quality.</p>

<p>You wouldn't trust someone selling you a Mercedes at a steep discount, so trust your same intuition when engaging with a software partner.</p>

<h3>Industry Trends</h3>

<p>You don't want to pay and develop your product only to find out that the technology underpinning your entire operation is outdated, misused, misguided, or improperly implemented.</p>

<p>By agreeing to a higher rate, you're allowing us the freedom to explore new industry trends, technologies, techniques, and best practices that are in turn reinvested in your business.</p>

<h3>Increased Focus</h3>

<p>We're afforded the luxury of focusing on our clients more directly due to the fact that we have the financial freedom to do so. When engaging at a lower rate, most often teams are driven to produce quickly, without focus, and sometimes without caution; economics dictate quantity over quality.</p>

<p>Conversely, we're able to take more time to keep your business goals in mind, ensure we're making the best use of technology, design, and budget to offer laser focus on crafting a solution that better fits your needs.</p>]]></content><author><name>Matt</name></author><category term="business" /><summary type="html"><![CDATA[As software craftspeople, we hear it quite often; how did we determine our hourly rate, or why can't we shave just a little off the cost for a client. It's an important discussion to have with any client, ensuring they see our value and what that rate provides.]]></summary></entry><entry><title type="html">Announcing OnTrac Point of Sale</title><link href="https://www.madebylotus.com/blog/announcing-ontrac-point-of-sale/" rel="alternate" type="text/html" title="Announcing OnTrac Point of Sale" /><published>2018-09-05T00:00:00+00:00</published><updated>2018-09-05T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/announcing-ontrac-point-of-sale</id><content type="html" xml:base="https://www.madebylotus.com/blog/announcing-ontrac-point-of-sale/"><![CDATA[<p>We're stupid excited to share our latest product launch with our partners at KARM Thrift Stores and SMCo Thrift in Knoxville TN. KARM (Knoxville Area Rescue Ministries) is the makers of the ThriftTrac™ product platform, enabling thrift store chains to better analyze and manage their organization.</p>

<p>With the introduction on OnTrac POS, ThriftTrac™ users will now have access to a powerful, intuitive and fully integrated Point of Sale offering empowering them to better capture, analyze and act upon metrics in their organization.</p>

<iframe src="https://www.youtube.com/embed/pKtfTr4sjoU" title="Lotus Apps - KARM OnTrac Point of Sale Case Study" width="560" height="314" style="max-width: 100%;" frameborder="0" allowfullscreen=""></iframe>

<p>We were delighted and honored to work with KARM to add such a powerful tool to the ThriftTrac™ platform. Through discovery, design, mobile development and creation of new mobile cloud APIs, we worked to bring their vision and product to market and couldn't be happier or more proud of the result!</p>

<p>We would be <a href="/portfolio/on-trac/">honored if you would read the case study</a> and let us know if we could be any help to your organization!</p>]]></content><author><name>Matt</name></author><category term="business" /><summary type="html"><![CDATA[We're stupid excited to share our latest product launch with our partners at KARM Thrift Stores and SMCo Thrift in Knoxville TN. KARM (Knoxville Area Rescue Ministries) is the makers of the ThriftTrac™ product platform, enabling thrift store chains to better analyze and manage their organization.]]></summary></entry><entry><title type="html">What You&apos;re not Asking your Development Partner</title><link href="https://www.madebylotus.com/blog/what-youre-not-asking-your-development-partner/" rel="alternate" type="text/html" title="What You&apos;re not Asking your Development Partner" /><published>2018-09-04T00:00:00+00:00</published><updated>2018-09-04T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/what-youre-not-asking-your-development-partner</id><content type="html" xml:base="https://www.madebylotus.com/blog/what-youre-not-asking-your-development-partner/"><![CDATA[<p>Choosing a partner to help build your startup or small business product can be a daunting task and is an important one. Collaborating with the right group will resonate with your values, product and customers to build a platform and product that is strong and vibrant. Working with the wrong group will slow momentum, distract, and keep your startup from reaching potential.</p>

<h2>What You're Asking Wrong</h2>

<p>You have a list of questions you ask every potential partner to narrow down the field. Are you asking the right questions?</p>

<h3>Can you give me an estimate without any background?</h3>

<p>Here's some insider info: estimating work is hard, whether you're remodeling a kitchen or building a software product. Estimates are of course useful, but don't trust an estimate from a partner when adequate exploration, discussion and discovery haven't been performed.</p>

<h3>What's the cheapest hourly rate?</h3>

<p>Price is obviously and important factor in partner with a development team, but focusing on an hourly rate is a misguided effort. Partner with a team that can deliver value, be it advice, leadership, or quality product and focus less on how much you'll pay per hour.</p>

<h2>What You're Not Asking</h2>

<p>What you're not asking of from your partner is just as important and often overlooked. We think the following are discussion points that should be part of every possible exploration with a new development team.</p>

<h3>Are you a silent partner or an advisor?</h3>

<p>Unless you're a seasoned software maker already, it pays to partner with experienced craftspeople to not only build, but advise. Ask to see if your potential partner will silently build what you ask like a drone, or continuously question each product decision by framing with your platform goals, customer user experience, and budget.</p>

<h3>How many clients stay long term?</h3>

<p>You want to work with a partner who has long-term happy clients. Don't just ask for a number of clients they still work with, but get a feel for how what that level of engagement is. A partner that is still actively involved with clients long term is a good sign.</p>

<h3>Do they turn away clients or projects?</h3>

<p>See if the potential partner would or has a history of avoiding relationships or projects for beliefs, technology, workload, or else. Sensing that the partner has a clear vision of what their core skillset and best value is important - working with a partner that could "do anything" might mean a lack of expertise to deliver overall.</p>]]></content><author><name>Matt</name></author><category term="entrepreneurship" /><category term="development" /><summary type="html"><![CDATA[Choosing a partner to help build your startup or small business product can be a daunting task and is an important one. Collaborating with the right group will resonate with your values, product and customers to build a platform and product that is strong and vibrant. Working with the wrong group will slow momentum, distract, and keep your startup from reaching potential.]]></summary></entry><entry><title type="html">Our Collaboration with MathStar</title><link href="https://www.madebylotus.com/blog/collaboration-with-math-star/" rel="alternate" type="text/html" title="Our Collaboration with MathStar" /><published>2018-08-16T00:00:00+00:00</published><updated>2018-08-16T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/collaboration-with-math-star</id><content type="html" xml:base="https://www.madebylotus.com/blog/collaboration-with-math-star/"><![CDATA[<p>We enjoyed collaborating with Carol at MathStar and the mobile development team at TouchTap earlier this year to discover, define and build a platform solution to help students and parents retain math skills during summer break.</p>

<p>We guided Carol through the process of really understanding what the product should do for market fit, price, competition, and the direction of the platform as a whole.  We worked with the team to use that guiding overview to deliver and launch a platform as the MathStar product.</p>

<p>Since launch in Q2 2018, we're happy to hear that almost 5k students are using the platform, engagement has been high, and students and parents both are having fun using the app and platform!</p>

<h2>The Case Study</h2>
<p>For more information on how we worked with Carol and TouchTap, you can read our more in depth case study below.</p>

<p><a href="/portfolio/math-star/" class="btn btn-theme btn-block">MathStar Case Study</a></p>]]></content><author><name>Matt</name></author><category term="business" /><summary type="html"><![CDATA[We enjoyed collaborating with Carol at MathStar and the mobile development team at TouchTap earlier this year to discover, define and build a platform solution to help students and parents retain math skills during summer break.]]></summary></entry><entry><title type="html">Announcing School PayIt</title><link href="https://www.madebylotus.com/blog/announcing-school-payit/" rel="alternate" type="text/html" title="Announcing School PayIt" /><published>2017-10-12T00:00:00+00:00</published><updated>2017-10-12T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/announcing-school-payit</id><content type="html" xml:base="https://www.madebylotus.com/blog/announcing-school-payit/"><![CDATA[<p>Every product launch is exciting, but we're really proud of the team's work to bring School PayIt to life.</p>

<p>School PayIt allows school administrators, parents, and students to stay connected and engaged, providing a digital platform to manage a school's student body, event and fundraising efforts, accept and track payments, permission slips and more.  Parents love electronically signing and paying for events with ease on the mobile phone.</p>

<p>We tracked over 450 user stories for the web application alone, across 20 deliverable milestones, 2300+ commits on 3 web codebases and over 1300 tests including Javascript integration testing.  Utilizing Google Hangouts, Slack, and TeamWork we managed to stay connected and deliver a large, well tested product platform on schedule within 2% of the budget and scope in less than 90 days.</p>

<div class="video-holder">
  <div class="video-wrapper">
    <div class="video-swap" data-video="https://player.vimeo.com/video/221410100?autoplay=1">
      <img width="1280" height="721" src="/static/assets/school-payit/video-preview.3f7f46c0f5.webp" alt="School PayIt Video Preview" />
      <div class="video-overlay"></div>
      <img width="207" height="210" src="/static/assets/school-payit/play-circle.b07544c127.png" class="play-circle" alt="" />
    </div>
  </div>
</div>

<h2>The Case Study</h2>
<p>We're so proud of our work we've put together a case study showing how we executed a laser focused plan to craft a robust platform in record time.</p>

<p><a href="/portfolio/school-payit/" class="btn btn-theme btn-block">SPI Case Study</a></p>]]></content><author><name>Matt</name></author><category term="ruby-on-rails" /><category term="iOS" /><category term="Android" /><summary type="html"><![CDATA[Every product launch is exciting, but we're really proud of the team's work to bring School PayIt to life.]]></summary></entry><entry><title type="html">Using Before Destroy and Has Many in Rails</title><link href="https://www.madebylotus.com/blog/before-destroy-with-has-many-in-rails/" rel="alternate" type="text/html" title="Using Before Destroy and Has Many in Rails" /><published>2017-08-03T00:00:00+00:00</published><updated>2017-08-03T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/before-destroy-with-has-many-in-rails</id><content type="html" xml:base="https://www.madebylotus.com/blog/before-destroy-with-has-many-in-rails/"><![CDATA[<p>A quick tip for utilizing a before_destroy callback in Rails alongside associations.</p>

<h2>The Problem</h2>

<p>It's not often we make use of "before_destroy" callbacks in Rails (in fact, we advocate for service objects); however, we found ourselves wanting to make use of this callback recently to process data in a has many relationship.</p>

<h3>Archive Association on Destroy</h3>
<p>In the following example, we want to perform some logic on any associated models using an <code>ActiveJob</code> worker, as well as ensure that each payment method is updated to remove the reference to this parent model.</p>

<script src="https://gist.github.com/macfanatic/7f698030ecc3a4d5fe19938943255f66.js"></script>

<p>With this code however, when our callback is executed, <code>payment_methods</code> will be empty, as the <code>dependent: :nullify</code> has already set the foreign key value to <code>null</code> for each.</p>

<h3>Prepend the Callback</h3>
<p>As it turns out, the <code>dependent</code> option on a <code>has_many</code> association is implemented as a callback itself, a <code>before_destroy</code> to be exact.  So while we could write a custom <code>before_action</code> that also handles the nullification of the foreign key, we couldn't easily reuse this existing service.</p>

<script src="https://gist.github.com/macfanatic/a99582985b56a95cc658cda507376d67.js"></script>

<p>In this scenario, we simply <code>prepend</code> our callback so it is executed before foreign key nullification occurs.</p>]]></content><author><name>Matt</name></author><category term="ruby-on-rails" /><summary type="html"><![CDATA[A quick tip for utilizing a before_destroy callback in Rails alongside associations.]]></summary></entry><entry><title type="html">Expanding Objects in Stripe API</title><link href="https://www.madebylotus.com/blog/expanding-objects-in-stripe-api/" rel="alternate" type="text/html" title="Expanding Objects in Stripe API" /><published>2017-08-01T00:00:00+00:00</published><updated>2017-08-01T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/expanding-objects-in-stripe-api</id><content type="html" xml:base="https://www.madebylotus.com/blog/expanding-objects-in-stripe-api/"><![CDATA[<p>Learn this quick tip to reduce the number of API requests and speed up your payments integration.</p>

<h2>Reducing API Requests</h2>

<p>Reducing the number of network calls in any web application is a great way to improve not only speed, but reliability of code.  Just imagine we have a worker unit that asks Stripe for a Customer, Card, and Invoice via three separate API requests.  Not only is this slower than making a single network call, it also triples our chance for errors or failures.</p>

<h3>First Attempt</h3>

<p>In this first attempt, we simply fetch the refund from stripe and ask for the balance transaction in a separate request.</p>

<script src="https://gist.github.com/macfanatic/42b9cf7f298c88fc577457c45e5c380b.js"></script>

<h3>Single Request</h3>

<p>We can definitely improve this by using the <code>expand</code> parameter to fetch the balance transaction at the same time as the refund.</p>

<script src="https://gist.github.com/macfanatic/d27e8c6d68f7e282d1d9c26e0f745b12.js"></script>

<p>Notice that we pass a Hash to <code>retrieve</code> instead of just an identifier.  We spent awhile debugging and digging through the Stripe Ruby gem source before noticing why the <code>expand</code> parameter was being ignored.</p>

<p>We were also sad to see that it is not possible to expand properties for a <code>Stripe::Event</code>.</p>

<h2>Stripe API Docs</h2>

<p>You can read more about expanding multiple objects on the official Stripe Docs.</p>]]></content><author><name>Matt</name></author><category term="ruby-on-rails" /><summary type="html"><![CDATA[Learn this quick tip to reduce the number of API requests and speed up your payments integration.]]></summary></entry><entry><title type="html">How We Approach a New Project</title><link href="https://www.madebylotus.com/blog/how-we-approach-a-new-project/" rel="alternate" type="text/html" title="How We Approach a New Project" /><published>2017-07-12T00:00:00+00:00</published><updated>2017-07-12T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/how-we-approach-a-new-project</id><content type="html" xml:base="https://www.madebylotus.com/blog/how-we-approach-a-new-project/"><![CDATA[<p>Learn how we discuss, analyze, suggest and provide a Project Assessment to new customers to guide and advise on new projects.</p>

<hr />

<h2>Reliable Contact</h2>
<p>Perhaps one of the very first tenants of a strong business relationship is building a sense of trust.  The easiest and most thoughtful way to build trust and simply provide good service, is to ensure we're timely in our communications, respectful of our customer's time, and clear in our intensions while offering simple action items to make this process as easy as possible.</p>

<h2>Introductory Meeting</h2>
<p>Sometimes we meet in person, but quite often this initial call is a roughly 30 minute video conference where we learn more about our customer's pain points and goals, budget, timeline, and share some of our experience and general thoughts.</p>

<p>The outcome of this session should be for us to evaluate if the customer is a good fit for us and if we want to proceed.  Most customers won't have enough information at this point to be able to make any decision about working with us or not.  A followup call is scheduled if all looks promising.</p>

<h2>Discovery Session</h2>
<p>If we've determined that the customer's goals seem technically achievable, align with our expertise, and have a reasonable timeline for the project, then at this stage we engage in a brief discovery session.</p>

<p>A discovery session is simply an opportunity for us to get undivided attention from the customer to walk us through what he hopes to achieve, while allowing us to probe with scenarios, raise concerns, and ask questions.  For small projects this might be 30-45 minutes, and for larger projects this could be spread out over a couple meetings to walk through everything.</p>

<p>The goal of the discovery session(s) is to ensure we have a clear understanding of <em>why</em> the customer is considering building a software solution, <em>what</em> he hopes to achieve by doing so, <em>how</em> he is going to deem the project a success, and <em>when</em> the project should be deployed and available to the public.</p>

<h2>Project Assessment</h2>
<p>Now that we have a clearer understanding of the project, we can begin to prepare a groundwork to work with the customer.  At this point we usually do not have an exhaustive list of requirements and functionality (although some clients are super prepared!), so we are focused on the business value and purpose the technology will provide, and don't get into the super detailed.</p>

<p>We deliver a Project Assessment to the customer, outlining the following items:</p>

<ul>
  <li>Project Background</li>
  <li>Goals</li>
  <li>Keys to Success</li>
  <li>Core Components</li>
  <li>Risk Factors (if applicable)</li>
  <li>Next Steps</li>
</ul>

<p>When the customer reviews our Project Assessment, he'll have a lot of information at his fingertips - a clear, understandable definition of the technology we envision solving his problem, our understanding of his problem and what we see as critical factors to ensure the success, and how proceeding in the process works.  Our clients love this, as this is a great educational tool and shows how committed we are to making what is a potentially scary investment, a success.</p>]]></content><author><name>Matt</name></author><category term="entrepreneurship" /><summary type="html"><![CDATA[Learn how we discuss, analyze, suggest and provide a Project Assessment to new customers to guide and advise on new projects.]]></summary></entry><entry><title type="html">Official Launch of Equus Hub for iOS</title><link href="https://www.madebylotus.com/blog/official-launch-of-equus-hub-for-ios/" rel="alternate" type="text/html" title="Official Launch of Equus Hub for iOS" /><published>2017-06-26T00:00:00+00:00</published><updated>2017-06-26T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/official-launch-of-equus-hub-for-ios</id><content type="html" xml:base="https://www.madebylotus.com/blog/official-launch-of-equus-hub-for-ios/"><![CDATA[<h2>About Equus Hub</h2>
<p>Equus Hub approached TouchTap to build an iOS application, allowing enthusiasts, riders, and professionals to network and sell equipment.</p>

<p>We were excited to build a custom mobile API allowing the mobile team to create a robust iOS application, for users to network, connect, sell, buy and find other individuals and businesses in the equestrian community.</p>

<p><img width="1360" height="725" src="/static/images/posts/official-launch-of-equus-hub-for-ios/equus-hub-friends.45329ae316.webp" class="img-responsive" alt="Hands holding an iPhone showing a listing in the Equus Hub app, with a second person holding a tablet behind" /></p>

<h2>Read the Case Study</h2>

<p>Coordinating a software launch is never easy, so we've written a case study on how we design, developed, and launched the Equus Hub mobile API.</p>

<p><a href="/portfolio/equus-hub/" class="btn btn-theme btn-block">Read the Equus Hub for iOS Case Study</a></p>]]></content><author><name>Matt</name></author><category term="ruby-on-rails" /><category term="iOS" /><summary type="html"><![CDATA[About Equus Hub Equus Hub approached TouchTap to build an iOS application, allowing enthusiasts, riders, and professionals to network and sell equipment.]]></summary></entry><entry><title type="html">Good Tech Disrupts Established Markets Easily - Trucker Path</title><link href="https://www.madebylotus.com/blog/trucker-path-tech/" rel="alternate" type="text/html" title="Good Tech Disrupts Established Markets Easily - Trucker Path" /><published>2017-06-19T00:00:00+00:00</published><updated>2017-06-19T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/trucker-path-tech</id><content type="html" xml:base="https://www.madebylotus.com/blog/trucker-path-tech/"><![CDATA[<h2>Tech is Important</h2>
<p>I love the story of <a href="https://truckerpath.com/">Trucker Path</a>, "Technology Built for the Trucking Industry".  Every industry has the opportunity for a new player to solve problems that others didn't see, or deemed impossible to overcome, and the trucking industry is an established and archaic industry benefiting from fresh thinking and technology.</p>

<h2>How do you Disrupt?</h2>
<p>Creating software solutions, web apps or mobile apps to solve a problem makes sense - but execution and identifying the key needs and problems of your target audience is paramount to disrupting established players and processes in a market.  Trucker Path identified several key problems for many different customers (user personas) and offers ways to ease a burden, whether that is a financial burden, a time restriction, or simply an annoyance.</p>

<p><img width="1360" height="906" src="/static/images/posts/trucker-path/truck-landscape.2de17d218b.webp" class="img-responsive" alt="Trucker Path - Landscape Hauler" /></p>

<h2>Truck Drivers</h2>
<p>What does Trucker Path solve for truck drivers? Trucker Path has over 550,000 of 1.6 Million (34% market share) of the drivers in the US on their app!</p>

<ul>
  <li>The biggest problem drivers report is easily finding a place to park their rig to sleep</li>
  <li>Connecting drivers with jobs</li>
  <li>Road conditions, fueling stations, etc</li>
  <li>Facilitating faster payments</li>
</ul>

<p>As a driver, I'd be thrilled to have a more complete picture of where I should refuel, sleep and avoid construction to keep my productivity on the road high, and maximize my time at home when not at work.  Additionally, standardizing and providing a marketplace connection to find work is huge.</p>

<h2>For Employers</h2>
<p>For employers, simply finding drivers at the right price and equipment can be a large task.</p>

<ul>
  <li>Find drivers with the right equipment, skill, and price for my job</li>
  <li>Easily monitor the job and delivery</li>
  <li>Easily pay upon completion</li>
</ul>

<h2>Conclusion</h2>
<p>If you have realized a pain point in an industry, no matter how small, utilizing mobile and web applications to create custom software solutions empowering your customers to engage, simplify, and enjoy their work and life more is a great business model indeed.</p>

<hr>

<p><a href="https://techcrunch.com/2017/06/14/trucker-path-just-lined-up-30-million-in-debt-to-lend-to-the-truckers-on-its-platform/" target="_blank" class="btn btn-theme btn-block">Read about Trucker Path on Tech Crunch</a></p>]]></content><author><name>Matt</name></author><category term="entrepreneurship" /><summary type="html"><![CDATA[Tech is Important I love the story of Trucker Path, "Technology Built for the Trucking Industry". Every industry has the opportunity for a new player to solve problems that others didn't see, or deemed impossible to overcome, and the trucking industry is an established and archaic industry benefiting from fresh thinking and technology.]]></summary></entry><entry><title type="html">WWDC 2017 and You</title><link href="https://www.madebylotus.com/blog/wwcd-2017-and-you/" rel="alternate" type="text/html" title="WWDC 2017 and You" /><published>2017-06-13T00:00:00+00:00</published><updated>2017-06-13T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/wwcd-2017-and-you</id><content type="html" xml:base="https://www.madebylotus.com/blog/wwcd-2017-and-you/"><![CDATA[<p>We're a week after the 2017 WWDC (World Wide Developers Conference) keynote and we've taken a few to digest the news, platform changes, and hardware announcements and share what we believe it means for the next year of mobile and Mac apps for your small business.</p>

<h2>VR is Huge</h2>
<p>Apple is betting big on Virtual Reality, by introducing a slew of technologies and hardware aimed at making developing VR apps for mobile or desktop as easy as possible.</p>

<p>Don't be surprised by the "VR Goldrush" in the App Store this fall when iOS 11 launches to the public.</p>

<h2>watchOS Steadily Improves</h2>
<p>For those of you in the wearable market, we enjoy seeing a steady stream of improvements to hardware and software enabling creators to craft more useful, and more user friendly, applications for customers.</p>

<h2>iPad Productivity</h2>
<p>We were a little surprised by the amount of iPad specific features released as part of iOS 11, but in a good way.  We believe in the power of the iPad, but honestly don't use it as a computer replacement - we spend all day on Macs writing and designing software, something still difficult to accomplish on a tablet.</p>

<p>However, if you have an iPad app, we'd strongly recommend making the investment to update your app to make use of the new productivity features, as we imagine customers will come to expect this very quickly and not supporting will lead to negative App Store reviews pretty quickly.</p>

<h2>Video and Photo Formats</h2>
<p>Not as amazing in itself, but Apple is rolling out support for new video and image codecs, offering up to 40% better compression without any loss of quality compared to predecessors (H264 for instance).</p>

<p>Our prediction? Apple is trying to find ways to offer users more space on device, and possibly help save on data transfer costs by reducing file size on our mobile devices.  Perhaps now is the time to come up with the "Uber for Instagram"?</p>]]></content><author><name>Matt</name></author><category term="entrepreneurship" /><summary type="html"><![CDATA[We're a week after the 2017 WWDC (World Wide Developers Conference) keynote and we've taken a few to digest the news, platform changes, and hardware announcements and share what we believe it means for the next year of mobile and Mac apps for your small business.]]></summary></entry><entry><title type="html">5 Wishes for Apple&apos;s 2017 WWDC</title><link href="https://www.madebylotus.com/blog/wwdc-2017-wishlist/" rel="alternate" type="text/html" title="5 Wishes for Apple&apos;s 2017 WWDC" /><published>2017-06-05T00:00:00+00:00</published><updated>2017-06-05T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/wwdc-2017-wishlist</id><content type="html" xml:base="https://www.madebylotus.com/blog/wwdc-2017-wishlist/"><![CDATA[<p>We took some time to poll the team and see what functionality or features we'd like most to see at today's WWDC keynote or unveiled during the conference week.  Enjoy!</p>

<hr />

<h2>Excitement for the Mac</h2>
<p>First and foremost, we love the Mac - always have, always will.  We make our living using Macs, we create software using our Macs, we <em>live</em> on our Mac.  While we enjoy iOS and have bought into the ecosystem long ago, we <strong>yearn</strong> for the days when the Mac truly saw major improvements and not just find ways to implement features from iOS.</p>

<p>What does this entail exactly? Surprise us, that's part of the fun.  Here's to hoping Apple has saw a problem that we didn't know we had, and solved it in an elegant way like they used to.</p>

<h2>Separate iTunes</h2>
<p>This has been written about many times, but this is a major usability issue in our opinion.  Using iTunes on our Mac to manage Netflix subscriptions, view account purchases, rent movies, buy music, browse apps - the list goes on and on for no good reason.  Give us separate apps to rent/buy content (and view our library) and move the rest of account management online to a web app with a better experience.</p>

<h2>New iOS Design Language</h2>
<p>iOS 7 brought a major design overhaul, and not in a good way.  Over subsequent releases the design has been refined, but we have stayed "flat" until app icons have little definition, and the Music app from Apple is a white background with large black text.</p>

<p>Please app, introduce more animation, colors, shadows, depth and <strong>purpose</strong> to our platform.  Google is nailing this with their <a href="https://material.io/guidelines/">Material Design</a> and we'd like to see what Apple can do.</p>

<h2>Major Xcode Update</h2>
<p>Asking for a major Xcode update is scary, with so much activity occurring on the Swift language itself making backwards compatibility of code a serious effort of dev teams around the world.</p>

<p>We'd like to see a serious focus on improving resource usage, responsiveness and preventing crashes or hangups.  Working on a small iOS project in Xcode 8 on the latest generation Apple laptop utilizes a vast amount of resources and is sometimes unusable.</p>

<h2>Rethink "web" apps on Mac</h2>
<p>I'm looking at you Slack.  I'm not sure what the solution is, but writing a wrapper around a web app is not a great experience for anyone, and Slack is a terrible resource hog.  While that is a competitive disadvantage and keeps the space open for a newcomer to deliver a truly stellar app experience on Mac, we think Apple could provide some tools, direction, and possibly even enforcement around the customer's experience.</p>]]></content><author><name>Matt</name></author><category term="entrepreneurship" /><summary type="html"><![CDATA[We took some time to poll the team and see what functionality or features we'd like most to see at today's WWDC keynote or unveiled during the conference week. Enjoy!]]></summary></entry><entry><title type="html">Aboriginal Apprenticeship Board of Ontario Portal Software Launch</title><link href="https://www.madebylotus.com/blog/aboriginal-apprenticeship-board-of-ontario-portal-software-launch/" rel="alternate" type="text/html" title="Aboriginal Apprenticeship Board of Ontario Portal Software Launch" /><published>2017-05-01T00:00:00+00:00</published><updated>2017-05-01T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/aboriginal-apprenticeship-board-of-ontario-portal-software-launch</id><content type="html" xml:base="https://www.madebylotus.com/blog/aboriginal-apprenticeship-board-of-ontario-portal-software-launch/"><![CDATA[<p>We're so very proud to have worked with our friends at <a href="http://www.animikii.com">Animikii, Inc</a> out of Victoria, British Columbia, to create a platform allowing employers, tradespeople, mentors and career seekers to connect and share experiences for the Aboriginal Apprenticeship Board of Ontario.</p>

<div class="row" style="margin-bottom: 25px;">
  <div class="col-sm-6 col-sm-offset-3">
    <img width="1360" height="2175" src="/static/images/posts/aboriginal-apprenticeship-board-of-ontario-software-launch/social-media-banner.7752c34b4e.webp" class="img-responsive" alt="A teal banner reading &quot;Launching the new AABO Portal of Ontario&quot;, with Lotus Apps below" />
  </div>
</div>

<p>This platform will provide an easy way for career seekers to connect with mentors in their field of expertise, employers to find talented and experienced tradespeople, and allow mentors to share their valuable experience with those just entering the respective field.</p>

<p>Coordinating a software launch is never easy, so we've written a case study on how we developed and launched the entire AABO portal and administrative tool in just 4 weeks, without designs or formal requirements and hope you'll read more!</p>

<a href="/portfolio/animikii-aabo/" class="btn btn-block btn-theme">Read the Animikii AABO Portal Case Study</a>]]></content><author><name>Matt</name></author><category term="ruby-on-rails" /><category term="project-management" /><summary type="html"><![CDATA[We're so very proud to have worked with our friends at Animikii, Inc out of Victoria, British Columbia, to create a platform allowing employers, tradespeople, mentors and career seekers to connect and share experiences for the Aboriginal Apprenticeship Board of Ontario.]]></summary></entry><entry><title type="html">Announcing Our Entrepreneur Video Series</title><link href="https://www.madebylotus.com/blog/announcing-our-entrepreneur-video-series/" rel="alternate" type="text/html" title="Announcing Our Entrepreneur Video Series" /><published>2017-04-20T00:00:00+00:00</published><updated>2017-04-20T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/announcing-our-entrepreneur-video-series</id><content type="html" xml:base="https://www.madebylotus.com/blog/announcing-our-entrepreneur-video-series/"><![CDATA[<p>We're really excited to offer something new and hopefully addictive (and of course helpful) - video series just for founders!</p>

<h2>Why a Video Series?</h2>
<p>We decided to create a video series for a few reasons.  First, a video will allow us to speak a little more informally and directly address questions you have or concerns you might encounter as you take the journey of creating software solutions.  Second, we felt that offering a video format for this premium content will allow us to connect with our founders much easier - we know schedules are hectic, and maybe the 5 minute video is the best way to reach you and get some important information to you!</p>

<h2>What to Expect</h2>
<p>We plan on releasing content every 1-2 weeks, and the video will always be free - although you'll want to register to stay in touch with the series and get the most out of the program.</p>

<h2>First Installment</h2>
<p>Simply put, here's the first video in our new entrepreneur series ready for your consumption!</p>

<iframe title="Lotus Apps - Series Intro" width="560" height="315" src="https://www.youtube.com/embed/LjSj9g8afJw" frameborder="0" allowfullscreen></iframe>]]></content><author><name>Matt</name></author><category term="entrepreneurship" /><summary type="html"><![CDATA[We're really excited to offer something new and hopefully addictive (and of course helpful) - video series just for founders!]]></summary></entry><entry><title type="html">5 Software Solutions that Failed with Poor Marketing</title><link href="https://www.madebylotus.com/blog/5-software-solutions-that-failed-with-poor-marketing/" rel="alternate" type="text/html" title="5 Software Solutions that Failed with Poor Marketing" /><published>2017-04-13T00:00:00+00:00</published><updated>2017-04-13T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/5-software-solutions-that-failed-with-poor-marketing</id><content type="html" xml:base="https://www.madebylotus.com/blog/5-software-solutions-that-failed-with-poor-marketing/"><![CDATA[<p>Successfully identifying your target audience and validating your market early on are key to launching your software solution successfully.  Read on to see failed software solutions and how to recover from a failed launch.</p>

<ul>
  <li><a href="/blog/3-tips-to-identify-the-target-audience-for-software-solutions/">Importance of identifying your target audience</a></li>
  <li><a href="/blog/why-market-validation-is-important-to-launching-software-solutions/">Why market validation is important to the launch of your software solution</a></li>
</ul>

<hr />

<h2>5 must reads for understanding failed software solution launches and how to recover</h2>

<ul>
  <li><a href="https://hbr.org/2011/04/why-most-product-launches-fail">Why most product launches fail</a></li>
  <li><a href="https://www.semrush.com/blog/marketers-fail-understand-target-audience/">Why Most Marketers Fail to Understand Their Target Audience</a></li>
  <li><a href="http://community.uservoice.com/blog/why-products-fail/">7 Reasons New Products Fail</a></li>
  <li>Four Marketing Mistakes You Will Make Without Demographic Research</li>
  <li><a href="https://blog.kissmetrics.com/recover-from-failed-product-launch/">How to recover from a Failed Product Launch</a></li>
</ul>]]></content><author><name>Matt</name></author><category term="entrepreneurship" /><summary type="html"><![CDATA[Successfully identifying your target audience and validating your market early on are key to launching your software solution successfully. Read on to see failed software solutions and how to recover from a failed launch.]]></summary></entry><entry><title type="html">3 Tips to Identify the Target Audience for Software Solutions</title><link href="https://www.madebylotus.com/blog/3-tips-to-identify-the-target-audience-for-software-solutions/" rel="alternate" type="text/html" title="3 Tips to Identify the Target Audience for Software Solutions" /><published>2017-04-10T00:00:00+00:00</published><updated>2017-04-10T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/3-tips-to-identify-the-target-audience-for-software-solutions</id><content type="html" xml:base="https://www.madebylotus.com/blog/3-tips-to-identify-the-target-audience-for-software-solutions/"><![CDATA[<p>Early on in your process of starting your new software project, you're spending a lot of time just figuring out who you should be selling your product to, and where they hang out and you're most likely to find them.</p>

<hr />

<h2>3 Tips to Identifying your Target Audience</h2>

<p>Correctly identifying your target audience is crucial to the successful launch of software solutions - in fact any business.  However, being based entirely online presents some unique challenges that traditional brick-and-mortar sellers can often overlook at the beginning.</p>

<h3>1. Describe the problem you are solving in 3 different ways</h3>
<p>Sit down and take the time to write out 3 distinct explanations for the problem that your new software product will solve.  Try to frame your explanations in terms that your customer might use when describing their problem, such as "how to save money on taxes next year" instead of "tax reducation or elimination strategy".</p>

<h3>2. Get feedback on your explanations</h3>
<p>Take your witten explanations of the problem to friends, family, and ideally people you have identified as potential a great fit for your product, and get feedback.  Take this feedback to heart and attempt to rework or create 3 new explanations of the problem.</p>

<h3>3. Identity your customer based upon their problem</h3>
<p>Now that you have 3 or more very clear and refined descriptions of the same problem that you are setting out to solve by creating a new software solution, take the time to reflect and describe who your ideal customer.  Are you creating a product that you think will best be adopted by users as a mobile app, or is this something complicated and requiring dedicated focus from a user at a desktop computer using a web browser?</p>

<p>Write out your customer description and again reflect.  Stay tuned for a listing of failed software solutions and how you can recover from a bad launch.</p>]]></content><author><name>Matt</name></author><category term="entrepreneurship" /><summary type="html"><![CDATA[Early on in your process of starting your new software project, you're spending a lot of time just figuring out who you should be selling your product to, and where they hang out and you're most likely to find them.]]></summary></entry><entry><title type="html">Why Market Validation is Important to Launching Software Solutions</title><link href="https://www.madebylotus.com/blog/why-market-validation-is-important-to-launching-software-solutions/" rel="alternate" type="text/html" title="Why Market Validation is Important to Launching Software Solutions" /><published>2017-04-06T00:00:00+00:00</published><updated>2017-04-06T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/why-market-validation-is-important-to-launching-software-solutions</id><content type="html" xml:base="https://www.madebylotus.com/blog/why-market-validation-is-important-to-launching-software-solutions/"><![CDATA[<p>Most people don't like homework, but that's exactly what we tell all our clients thinking of launching a new product with Lotus - do your homework!</p>

<hr />

<p>There are many steps to researching and preparing a plan to launch a new tech startup or software solutions business, and market validation is an important part of that process.</p>

<h2>What is Market Validation?</h2>
<p>It might sound complicated (and it can be) but the concept is simple: prove that there is a need for your software solution and people are willing to pay for it.</p>

<h2>Why is it important?</h2>
<p>You've identitified a need in the market and just know you have the best solution out there.  That's great!  However, if you can't find customers willing to pay for it, or to pay the price you might need to run your business, then your efforts are not going to end well.</p>

<p>Part of market validation is ensuring that you are even indeed trying to seduce the right customer.  Just imagine - you've got a great idea, know that your competitors aren't doing it well, and the industry is ripe for your entry.  However, what if your target audience is incorrect?</p>

<p>You wouldn't be the first entrepreneur to go after the wrong customer, costing you time, money, and fatigue - all things you are already short on as a small business owner.</p>

<h2>So, how do I do it correctly?</h2>
<p>If it's so important that you do market validation early in the business process, then how do you tackle it correctly? Well, we'll tackle that in a future post.</p>]]></content><author><name>Matt</name></author><category term="entrepreneurship" /><summary type="html"><![CDATA[Most people don't like homework, but that's exactly what we tell all our clients thinking of launching a new product with Lotus - do your homework!]]></summary></entry><entry><title type="html">We&apos;ve Rebranded</title><link href="https://www.madebylotus.com/blog/weve-rebranded/" rel="alternate" type="text/html" title="We&apos;ve Rebranded" /><published>2017-04-04T00:00:00+00:00</published><updated>2017-04-04T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/weve-rebranded</id><content type="html" xml:base="https://www.madebylotus.com/blog/weve-rebranded/"><![CDATA[<p>As we evolve and grow as a company we want to show a better view of ourselves and what we do. We are currently undertaking many changes in marketing and branding to become more visible to a greater audience.</p>

<p>We are proud of our extensive expertise of apps both web and mobile and want to feature more of that in our branding and marketing.</p>

<p><strong>This is the new face of Lotus Apps:</strong></p>

<p><img width="976" height="248" src="/static/images/posts/weve-rebranded/weve-rebranded.24c7c86d68.png" class="img-responsive" alt="The new Lotus Apps logo: a command symbol over a blue rounded square, forming a flower between the words Lotus and Apps" /></p>

<p>Through our rebranding we wanted to stick with our flower roots but have some technical meaning. After several rounds of concepting we came across the idea for the app icon (the blue rounded square) and the command symbol merging to make a top view of a flower. Its a more abstract concept than our previous logo. We believe moving forward this new brand can lead us to new heights while providing the same amazing experience for our clients.</p>

<p><strong>Thank you for being part of this journey!</strong></p>

<h3>For more information on branding and this project check out:</h3>

<p><a href="/portfolio/lotus-rebranding/">Lotus Case Study</a></p>

<p><a href="/blog/the-design-process/">The Design Process</a></p>

<p><a href="/blog/brand-identity-examples/">Understanding the basics of Branding through Brand Identity Examples</a></p>]]></content><author><name>Chris</name></author><category term="entrepreneurship" /><summary type="html"><![CDATA[As we evolve and grow as a company we want to show a better view of ourselves and what we do. We are currently undertaking many changes in marketing and branding to become more visible to a greater audience.]]></summary></entry><entry><title type="html">3 Tips to Create New Software Solutions</title><link href="https://www.madebylotus.com/blog/3-tips-to-create-new-software-solutions/" rel="alternate" type="text/html" title="3 Tips to Create New Software Solutions" /><published>2017-03-29T00:00:00+00:00</published><updated>2017-03-29T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/3-tips-to-create-new-software-solutions</id><content type="html" xml:base="https://www.madebylotus.com/blog/3-tips-to-create-new-software-solutions/"><![CDATA[<p>If you've ever thought about creating a new software product, either for your existing business or something new entirely - then let's explore what creating a new software solution entails and a few tips to get you on your way.</p>

<hr />

<h2>3 Tips to Create New Software Solutions</h2>
<p>Creating something entirely new is hard work, but here are a few quick tips to get your brain in the right mindset and explore the possibilities.</p>

<h3>1. Research Existing Solutions</h3>
<p>Before you get all excited and waste the next days, months or years of your life - do some basic research.  You don't want to begin a tech business dominated by multiple existing players, but you also need to be wary if you can't find any competitors.</p>

<p>Why you ask? Well you might be on to something entirely fresh and new (and we hope so!) but you might also have an idea that simply isn't technically possible, or affordable.</p>

<h3>2. Discuss - then discuss some more</h3>
<p>People get all caught up on not sharing their business idea with others, and there is some truth to that.  You certainly don't want to share with someone who will take it and run.  However, you do need some feedback, and not just validation from immediate friends and family.</p>

<p>Take some time to reach out to people in the industry or target market and find out if they perceive the need and would value a software solution that solved that need.  If you can't find anyone who has the problem, or who wants to pay for it, you're starting a losing battle.</p>

<h3>3. Sleep on it</h3>
<p>Don't rush into building a new software solution - there is a lot of work and planning that needs to go in to help ensure you're building an affordable solution that your customers want.</p>

<p>Working with an experienced software development company is a great place to start, but you'll still need to put a lot of time into communicating your idea to your new vendor.</p>]]></content><author><name>Matt</name></author><category term="entrepreneurship" /><summary type="html"><![CDATA[If you've ever thought about creating a new software product, either for your existing business or something new entirely - then let's explore what creating a new software solution entails and a few tips to get you on your way.]]></summary></entry><entry><title type="html">How to Make Marketing Yourself Easy</title><link href="https://www.madebylotus.com/blog/how-to-make-marketing-yourself-easy/" rel="alternate" type="text/html" title="How to Make Marketing Yourself Easy" /><published>2017-03-28T00:00:00+00:00</published><updated>2017-03-28T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/how-to-make-marketing-yourself-easy</id><content type="html" xml:base="https://www.madebylotus.com/blog/how-to-make-marketing-yourself-easy/"><![CDATA[<h3>The Key to Marketing</h3>

<p>The key when you are marketing yourself isn't exactly about you. It is about the clients needs and how <strong>YOU</strong> can help them. Be specific with who you are reaching out to. Know who your idea client is and talk to them. Don't be afraid to be specific. You'll have a greater chance at getting clients specifically targeting them versus being too general and getting no ones attention.</p>

<h3>How to better market to your customers or clients on your website</h3>

<p><img src="/static/images/posts/how-to-make-marketing-yourself-easy/social-media-balloons.8b8cc5b3b9.webp" class="img-responsive" width="320" height="357" style="width: 320px;" alt=""></p>

<p>There are a few things that you can do you to your website to make sure you are providing the right info to you customers or clients. Y</p>

<ul><li><strong>Show the customer or client of your Value</strong> - when you are talking about your features or services talk about how they will benefit the client and try to avoid jargon that will limit the client or customer understanding.</li><li><strong>Pricing</strong> - offering clear pricing and details of that on your page can help show the customer that you do it enough to have the pricing worked out.</li><li><strong>Create an offer</strong> - offer them something for free such as a white paper or a email course. Something that is going to benefit them and get them to want to provide their information.</li><li><strong>CTAs</strong> - make sure to have a CTA on every page. You want to give your customers or clients the most opportunities to help them and to collect their information.</li><li><strong>Portfolio (if you have one)</strong> - if you are a service company and have a portfolio of work don't just show pictures. Explain how you helped improve your clients product or service.</li><li><strong>Contact Page</strong> - don't put "get in touch" on your website. No matter what if the customer or client is contacting you they probably have a question about something. Have the field heading be "Got a question?" because you have the answer for them.</li></ul>]]></content><author><name>Chris</name></author><category term="marketing" /><summary type="html"><![CDATA[The Key to Marketing]]></summary></entry><entry><title type="html">How to Use Slack Voting</title><link href="https://www.madebylotus.com/blog/how-to-use-slack-voting-in-messaging/" rel="alternate" type="text/html" title="How to Use Slack Voting" /><published>2017-03-21T00:00:00+00:00</published><updated>2017-03-21T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/how-to-use-slack-voting-in-messaging</id><content type="html" xml:base="https://www.madebylotus.com/blog/how-to-use-slack-voting-in-messaging/"><![CDATA[<h2>Slack</h2>

<p><a href="https://slack.com">Slack</a> is a messaging tool used for businesses and organizations for better communication. You can separate projects or teams into channels. It allows for direct messaging along with audio and video calling. Slack also offers Web, Mobile, and desktop apps which makes staying on top of the project easy from anywhere. It is easy to learn how to use slack especially for voting and polling.</p>

<h2>Voting</h2>

<p>Teams or projects always have decisions that need to be made. Voting or Polling is a great in making some of these decisions. There are a few fun ways to do this in these. One is just in the app with reactions and the other is with an add on called <a href="https://simplepoll.rocks">Simple Poll</a> which allows you to easily create polls in the app.</p>

<h3>Reaction Voting</h3>

<p>Reactions are very simple and allow a user to react in any way they want to a message. In doing this you probably want to put a system in place such as 👍 is a yes and is a 👎 no.</p>

<p><img src="/static/images/posts/how-to-use-slack-voting-in-messaging/reactions.731e84065c.webp" class="img-responsive" width="640" height="555" alt="A Slack message with the reaction picker open, and the same message with a thumbs-up reaction added"></p>

<p>As you can see here I sent a message in the general channel. I click on the face to the right of the message and pick the reaction that I want.</p>

<p>You can also create custom emojis to use as reactions. If you click on the # at the top of the reactions there is a link to add your own. This is going to take you to Slack's web app. Here you can upload the image you are wanting to use and the command to use it. For a reaction you will always have to go into that last tab, #, and click on the new icon/emoji you have created.</p>

<p><img src="/static/images/posts/how-to-use-slack-voting-in-messaging/custom.51e787d4ef.webp" class="img-responsive" width="320" height="375" style="width: 320px;" alt="Slack's reaction picker with the custom emoji tab and the add custom emoji link circled"></p>

<p>Slack has various short cuts. To get a 😬 you can simply type :grinning: and it will pop up. If you want to use your new emoji for things other than reactions try to create a simple name.</p>

<p>The more people use the same reaction the number will go up to show a vote.</p>

<h3>Polling</h3>

<p><a href="https://simplepoll.rocks">Simple Poll</a> is a simple add on to slack that makes polls very easy. Simply go to <a href="https://simplepoll.rocks">Simple Poll</a> and click add to slack. Using the add on is easy and all done in your message. You simple start but using the / to pull up all the magical list of things you can do in slack. You will type /poll and then you question and the answers each in a "". Look at the follow example.</p>

<p><img src="/static/images/posts/how-to-use-slack-voting-in-messaging/polling.0d348d44c6.webp" class="img-responsive" width="640" height="631" alt="Typing a /poll command in Slack with a question and four answers in quotes, and the poll Simple Poll posts"></p>

<p>Once everyone clicks which number they want to vote for in the bottom you can see it tally up. This is great for getting those quick decisions made and not holding back your team or project.</p>

<p><img src="/static/images/posts/how-to-use-slack-voting-in-messaging/poll-made.294a37c32b.webp" class="img-responsive" width="348" height="266" style="width: 348px;" alt="A finished Simple Poll asking how easy it is to create a poll in Slack, with one vote counted"></p>]]></content><author><name>Chris</name></author><category term="marketing" /><category term="entrepreneurship" /><category term="design" /><category term="development" /><summary type="html"><![CDATA[Slack]]></summary></entry><entry><title type="html">How to Create Threadsafe Ruby Sidekiq Workers</title><link href="https://www.madebylotus.com/blog/how-to-create-threadsafe-ruby-sidekiq-workers/" rel="alternate" type="text/html" title="How to Create Threadsafe Ruby Sidekiq Workers" /><published>2017-03-16T00:00:00+00:00</published><updated>2017-03-16T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/how-to-create-threadsafe-ruby-sidekiq-workers</id><content type="html" xml:base="https://www.madebylotus.com/blog/how-to-create-threadsafe-ruby-sidekiq-workers/"><![CDATA[<h2>Creating Threadsafe Ruby Sidekiq Workers</h2>
<p>Thread safety in general can be difficult to wrap your head around at times, but at the simplest level if you have data shared between threads then things can get messy.  As always, concrete examples are easier than theory!</p>

<h2>Our Legacy Rails 3.2 Application</h2>
<p>In this legacy application, we were running the web server using passenger, which used worker processes and not threads, for concurrency.  This means that each process had its own memory, and writing threadsafe code wasn't a concern.  Similarily, the background queue processor was Resque, again using processes over threads for concurrency.</p>

<p>In our upgrade to newer hardware, we decided to leave the web server with passenger, but wanted to make the switch to Sidekiq for a myriad of reasons.  However, we encountered issues with our code not being threadsafe and that led to a few problems.</p>

<h2>Class Variables Are Not Threadsafe</h2>
<p>Don't use class variables in ruby, they just always cause problems.  In our case the code was storing whether the current temperature unit should be Celsius or Farenheit, scoped to a block of code.  Other examples might include a multi-tenant application and scoping to the current organization.  Our class used instance variables, meaning that if two threads attempted to modify the temperature unit, the later running thread "won".  This caused temperature units to be mixed up between workers, primarily when creating PDF reports to send via email.</p>

<script src="https://gist.github.com/macfanatic/8376a01373b518b21f9ff806e87d72b7.js"></script>

<h2>Using Thread.current</h2>
<p>Thread.current allows you to store information that is scoped to only the running thread, which is great for keeping data unique across a multithreaded environment such as Sidekiq (or app servers such as puma).  The problem, however, is that Thread.current will retain this information until the thread is killed, or the data is manually cleared.</p>

<p>In the web server (again, say puma), each request is served with a single thread.  However, that thread can persist to serve the next incoming request, and bam, your data is leaked across requests.  However, there is a solution.</p>

<h2>Using Request Store</h2>
<p>Request Store is a library that wraps Thread.current, but injects some Rack middleware into your web application so that the Thread.current store is cleared out after each request is served.  This is a clean, abstract way to ensure that your data is scoped per request, per thread.  Awesome, right? This is great for the web server, but doesn't actually ensure we aren't leaking data in our background jobs with Sidekiq - there is no request in a background worker.</p>

<h2>Using Request Store and Sidekiq</h2>
<p>In fact, this was such a common theme in our work that we decided to release a gem that integrates Request Store and Sidekiq together in an elegant way, piggy backing on the work already completed in Request Store.  In our gem, we create a piece of Sidekiq server middleware that is ran every time a job is pulled from the Sidekiq queue and processing begins.  The middleware ensures that the Request Store store is cleared after the job completes, just like Request Store does automatically with the Rack middleware it includes by default.</p>

<h2>Conclusion</h2>
<p>Now that we have a way to securely store information either per thread and either per web request or per Sidekiq job, our code is truly multithreaded and secure.  Below is an example of what our service was refactored to.</p>

<p>If you are interested in tuning your Sidekiq installation or ensuring process supervision when deployed, we have articles for you!</p>

<script src="https://gist.github.com/macfanatic/2b465dce2c35bbae999d1907a2a35d52.js"></script>]]></content><author><name>Matt</name></author><category term="ruby-on-rails" /><category term="programming" /><summary type="html"><![CDATA[Creating Threadsafe Ruby Sidekiq Workers Thread safety in general can be difficult to wrap your head around at times, but at the simplest level if you have data shared between threads then things can get messy. As always, concrete examples are easier than theory!]]></summary></entry><entry><title type="html">MetaProgramming Ruby to Manage Raspberry PI GPIO</title><link href="https://www.madebylotus.com/blog/metaprogramming-ruby-to-manage-raspberry-pi-gpio/" rel="alternate" type="text/html" title="MetaProgramming Ruby to Manage Raspberry PI GPIO" /><published>2017-03-14T00:00:00+00:00</published><updated>2017-03-14T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/metaprogramming-ruby-to-manage-raspberry-pi-gpio</id><content type="html" xml:base="https://www.madebylotus.com/blog/metaprogramming-ruby-to-manage-raspberry-pi-gpio/"><![CDATA[<p>In this post, we’re going to examine how we can easily describe all of the possible states for an indicator (think LED light) on a device and how we can most cleanly represent that definition by using metaprogramming ruby to manage the Raspberry Pi GPIO pin state.</p>

<h2>What is metaprogramming?</h2>
<p>The term sounds fancy, but one can think of it simply as the act of defining code that defines code. There are many examples of this in Ruby on Rails. Anytime you use a macro that will define additional methods, you are metaprogramming ruby.</p>

<p>We’re not just going to use methods someone else has defined to make our life easier, we’re going to write the methods ourselves. In fact, we’re going to create an entire DSL (<a href="https://en.wikipedia.org/wiki/Domain-specific_language">Domain Specific Language</a>) for modeling an Indicator.</p>

<h2>What is a GPIO Pin?</h2>
<p>Raspberry PI GPIO pins are specific to a Raspberry Pi, and stand for “General Purpose Input Output” pin. They are physically on the device, and can either be on, off, or what we’ll call a “nil” value. When programming on the RaspberryPI, we can use software to inspect the value of the pin at any given time, as well as change the value.</p>

<p>In our application, setting the value of a pin is actually providing voltage to an LED indicator, so by changing the value from off to on for particular pin(s), we can change the color of an indicator.</p>

<h2>Indicator Truth Table</h2>
<p>First, let’s examine a quick truth table that shows what state the indicator will be in, when the corresponding Raspberry Pi GPIO pins are configured.</p>

<pre><code>Indicator Name | State  | PIN 1 | PIN 2 | PIN 3 | PIN 4 | PIN 5 |
-----------------------------------------------------------------
Status         | OFF    |       |       |       |       |       |
               | BLUE   | ON    |       |       |       |       |
               | GREEN  | ON    | ON    |       |       |       |
Motor          | OFF    |       |       |       |       |       |
               | YELLOW |       |       | ON    |       |       |
               | RED    |       |       | ON    | ON    |       |
Power          | OFF    |       |       |       |       |       |
               | GREEN  |       |       |       |       | ON    |
</code></pre>

<p>In the above table, we have 3 different indicators. “Status” can be off, blue or green; “Motor” can be off, yellow or red; “Power” can be either off or green.</p>

<h2>Overall Software Design</h2>
<p>For this post, we’re going to just be focusing on a piece of the overall system to manage indicators on the device. The entire solution includes a long-running ruby process that acts as a monitor, to update the Raspberry Pi GPIO pin values every second.</p>

<p>The monitor process was built to simply schedule a Sidekiq worker repeatedly, with the Sidekiq worker actually inspecting the desired state and manipulating the value of GPIO pins so that the physical LED reflects that desired state.</p>

<p>We’re going to use redis to store the state of each indicator at any given point in time, have a service to place an indicator into a particular state (think write to redis) and then the Sidekiq worker will ask redis for the current state and configure the actual hardware.</p>

<h2>Simplest Attempt</h2>
<p>Our first attempt should indeed be the simplest, so we’re going to write a single class to represent the “Status” indicator. Once we have this written, we can begin abstracting as things become clearer.</p>

<script src="https://gist.github.com/macfanatic/9f5033fb4262bc20cfbfa638d10fe140.js"></script>

<p>In the above example, there are a few things I’d like to point out:</p>

<ul>
  <li>I’ve already introduced some concept of defining the possible states, and what those look like, as class level methods</li>
  <li>We’ve provided the beginning of a method to transition to a particular state</li>
</ul>

<p>Using this indicator, we might write something like this elsewhere in our codebase to update the state of the indicator.</p>

<pre><code>indicator = StatusIndicator.new
indicator.transition!(StatusIndicator::CHARGED)
</code></pre>

<p>We’ll keep this interface as we refactor and refine how we define our Indicator behavior below.</p>

<h2>Introducing a Superclass</h2>
<p>Knowing that I have more indicators to write (in this example two more, but our real world application had several more), I already know I’m going to want to share as much logic as possible by using inheritance.</p>

<p>With that in mind, I’ll rewrite to introduce a parent class and a subclass.</p>

<p>The most natural refactor to introduce a base class looks something like this:</p>

<script src="https://gist.github.com/macfanatic/d1af5ecfbffa02d2c682bf62200d57d3.js"></script>

<p>Examining this example, I can see that for each indicator I’m going to be defining a hash of states descriptions. I also need to have code that defines how to actually configure each Raspberry Pi GPIO pin to place the indicator into service.</p>

<h2>Adding a DSL</h2>
<p>With the goal of making it easy to define additional indicators, all of which need to define states and how those states are actually rendered, let’s look at introducing the following DSL to define an indicator succinctly.</p>

<script src="https://gist.github.com/macfanatic/8ddd3e1fe14656a4e407653af9371bb9.js"></script>

<p>Let’s take a look at the above class in a few sections to get a better idea of what is going on here.</p>

<h3>Defining the States</h3>
<p>In the above example, most of the magic occurs within the “define” class-level method that accepts a block.</p>

<pre><code>define do |router|
  router.on OFF, color: :off, blinks: false
end
</code></pre>

<p>In this snippet, we’ve introduced a simple way to define what the indicator should look like when it is off. This has replaced the class methods of “renderings” and “states” that we had previously.</p>

<h3>Defining GPIO Representation</h3>
<p>We didn’t bother coding a way to map a state to actual GPIO values in our simplest examples, but we’ve included that mapping here.</p>

<pre><code>define do |router|
  router.when :blue do
    enable_pin(GPIO_PIN_BLUE)
    disable_pin(GPIO_PIN_GREEN)
  end
end
</code></pre>

<h3>DSL Benefits</h3>
<p>It should be immediately clear, but readability has increased tremendously when viewing our indicator. The class is simply concerned with defining the possible states, and how those would be rendered.</p>

<p>The GPIO definition also maps nicely back to our truth table. You can see that in order to render a blue color, we expect the blue pin to be enabled, and the green pin to be disabled.</p>

<h2>Implementing with Meta-Programming</h2>
<p>In order to make the magic above happen, we’ll have to define several class methods and attributes to ensure that we can configure our indicator subclass with such ease.</p>

<script src="https://gist.github.com/macfanatic/c7fe6ee983c4cfdb2c8f3525c2cb2c5d.js"></script>

<p>In this parent class, we are creating several features for each subclass to use.  The first is the definition of the “define” method:</p>

<pre><code>def self.define(&amp;block)
  router = Router.new
  block.call(router)
  self.state_machine = router
end
</code></pre>

<p>This simply provides a way for the subclass to configure an instance of the router class, which is stored on the parent class as a class attribute to be accessed later (called “state_machine”).</p>

<p>The router is a complex little beast, responsible for defining the available states, how those are rendered, and a method to actually perform the rendering.</p>

<p>Below we can see that we’re expecting every Indicator subclass to be able to render itself as off, so that’s included as a default state in the router.</p>

<pre><code>class Indicator::Router
  def initialize
    on(Indicator::OFF, color: :off, blink: false)
  end
end
</code></pre>

<p>When defining what an Indicator should do to transition to a state, the router simply accepts a block to be called later on.</p>

<pre><code>class Indicator::Router
  def when(color, &amp;block)
    renderings[color.to_s] = block
  end
end
</code></pre>

<p>Now that the router contains the definition of all states and how to render, the Indicator itself defines a method that wraps this up nicely so rendering an Indicator from a particular state is easy.</p>

<pre><code>class Indicator
  def render(status)
    if visually_off?(status)
      turn_off
      return
    end
    case status.color
    when 'red' then turn_red
    when 'green' then turn_green
    when 'yellow' then turn_yellow
    when 'blue' then turn_blue
    end
  end
end
</code></pre>

<p>The implementation of “turn<em>red” or “turn</em>blue” is defined dynamically (it’s meta-programming, right?) by finding the block stored earlier when calling “when” on the router, and executing that block.</p>

<pre><code>class Indicator
  %w(off red green yellow blue).each do |state|
    define_method("turn_#{ state }") do
      render_proc = state_machine.renderings[state]
      raise "Unknown how to render color: #{ state } for LED: #{ self.class.name }" if render_proc.nil?
      instance_exec(&amp;render_proc)
    end
  end
end
</code></pre>

<h2>Putting it all Together</h2>
<p>To put all this together, we’ve made it really easy to instantiate a “Router” instance, configure the router, and store that per child class. Then when calling “transition!” or “render” on the child class, the implementation looks at the state_machine definition from the router to execute a proc or lookup details from a Hash to interface with the actual hardware.</p>

<h2>Putting to Use</h2>
<p>When this definition is complete, what does it look like to render an Indicator from our monitor process? Super simple:</p>

<script src="https://gist.github.com/macfanatic/adbead740b1d87ec5975879d46578ad9.js"></script>

<p>Our final interface to interact with our Indicators boils down to this:</p>

<pre><code># in our project
indicator = StatusIndicator.new
indicator.charging! # calls transition! storing state in redis
# change to another state, watch the indicator update
indicator.charged!
# in separate monitor (background) process
every 1.second do
  renderer = RenderIndicatorStatus.new
  renderer.perform(indicator.class.name)
end
</code></pre>

<h2>Testing with Rspec</h2>
<p>Testing with RSpec was also a breeze and readable with this approach. We tested the heck out of the base Indicator class as it interfaced with other existing services to manipulate hardware values (using mocks). To test the actual definition of the state and rendering, we wrote a custom matcher as well as some shared examples for a readable test such as:</p>

<script src="https://gist.github.com/macfanatic/3d68d283fc954274e110503de300ea3b.js"></script>

<h2>Sidekiq Deployment and Process Supervision</h2>
<p>In order to deploy and use this application code on our Raspberry Pi, we need to ensure that both our monitor process, sidekiq, and even redis are available and running when the device boots.  Read our related posts "<a href="/blog/use-sidekiq-without-rails/">How to use Sidekiq without Rails</a>" and "<a href="/blog/manage-ruby-processes-using-systemd-at-launch-on-linux/">How to Manage Ruby Processes at Launch</a>" for much more on monitoring and deploying this software setup on your Raspberry Pi.</p>

<h2>Wrapping Up</h2>
<p>This was a fun problem to solve for our client, allowing for very readable indicator definitions for peer review, and for any changes that may arise if we decide to configure the LED indicators to perform differently in the future.</p>]]></content><author><name>Matt</name></author><category term="ruby" /><category term="raspberry-pi" /><category term="programming" /><category term="IoT" /><summary type="html"><![CDATA[In this post, we’re going to examine how we can easily describe all of the possible states for an indicator (think LED light) on a device and how we can most cleanly represent that definition by using metaprogramming ruby to manage the Raspberry Pi GPIO pin state.]]></summary></entry><entry><title type="html">What is the Internet of Things or IoT? (Part 2)</title><link href="https://www.madebylotus.com/blog/what-is-the-internet-of-things-or-iot-part-2/" rel="alternate" type="text/html" title="What is the Internet of Things or IoT? (Part 2)" /><published>2017-03-09T00:00:00+00:00</published><updated>2017-03-09T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/what-is-the-internet-of-things-or-iot-part-2</id><content type="html" xml:base="https://www.madebylotus.com/blog/what-is-the-internet-of-things-or-iot-part-2/"><![CDATA[<h2>The Power of the Internet of Things</h2>
<p>In our last post "What is the Internet of Things", we examined the basic definition and concerns with connected "Internet of Things" devices.  While smart fridges or thermastats are cool, and certainly helpful around the house at times, we find ourselves drawn to answering the question "what else can I do with an IoT device?".</p>

<h2>Eyrus: A Case Study</h2>
<p>We have actually already used IoT to solve a challenging problem, for a company called Eyrus.  Eyrus provides real-time analytics tracking equipment and personnel at large construction sites.  Essentially they can know if equipment leaves the premises, and know when Joey was on the site - all helpful things when you consider employee timesheets, job costing, theft prevention, emergency management and all the features that the Eyrus system provides construction management.</p>

<p>How can a system have all that information?  Simple: IoT devices.  At Lotus, we worked closely with our custom hardware vendor partner to create enclosures that house power, processing, networking and antennas that run our Ruby software application.  As people or equipment move near an antenna, RFID tags are recorded and our Internet of Things devices take that data and relay to the cloud web application we created.</p>

<p>From there, the data is analyzed and reported upon for managers to use in a myriad of ways.</p>

<p><a href="/portfolio/eyrus-construction-analytics/">Interested in reading more about our work with Eyrus? Read the full case study.</a></p>

<h2>Mogul: A Case Study</h2>
<p>We're not new to this IoT movement - we have another project under our belt.  Mogul Wireless is a temperature and humidity monitoring company providing real time monitoring, alerting, and compliance for health care professionals.  Our IoT devices are deployed in hospitals and doctors offices on the west coast, constantly sampling temperature, humidity, and air pressure data to report to our cloud web application.</p>

<p>When a temperature or humidity value recorded is outside of a predefined range, then users can be alerted via text message, email, or automated phone call to resolve the issue.  All the data (and there is lots of it) is recorded and kept for compliance purposes.</p>

<p><a href="/portfolio/mogul-wireless/">Read the full case study of our work with Mogul here.</a></p>

<h2>Monitoring the Monitors</h2>
<p>Here's a question for you - who is monitoring our software to ensure that we have applied important security patches and have a system that is running as robustly as possible?  For web, mobile, and even IoT - Prospector is still a great way to track the dependencies in the project and we make use of Prospector to stay ahead of the competition.</p>]]></content><author><name>Matt</name></author><category term="IoT" /><summary type="html"><![CDATA[The Power of the Internet of Things In our last post "What is the Internet of Things", we examined the basic definition and concerns with connected "Internet of Things" devices. While smart fridges or thermastats are cool, and certainly helpful around the house at times, we find ourselves drawn to answering the question "what else can I do with an IoT device?".]]></summary></entry><entry><title type="html">What is the Internet of things or IoT? (Part 1)</title><link href="https://www.madebylotus.com/blog/what-is-the-internet-of-things-or-iot-part-1/" rel="alternate" type="text/html" title="What is the Internet of things or IoT? (Part 1)" /><published>2017-03-07T00:00:00+00:00</published><updated>2017-03-07T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/what-is-the-internet-of-things-or-iot-part-1</id><content type="html" xml:base="https://www.madebylotus.com/blog/what-is-the-internet-of-things-or-iot-part-1/"><![CDATA[<h2>What is the "Internet of Things"</h2>
<p>Buzzwords are super popular in tech circles, but keeping track of the latest trends and jargon is what we do best!</p>

<h3>Definition</h3>
<p>Google provides the following definition of the term:</p>

<p>the interconnection via the Internet of computing devices embedded in everyday objects, enabling them to send and receive data.</p>

<h3>Practical Application</h3>
<p>In practical terms, a device connected to the internet is coined an IoT device.  One could argue that a Personal Computer (PC) is inherently an IoT device, but PCs are excluded from the category simply because we're all familiar with what a PC in 2017.</p>

<p>Examples of IoT devices in your home would include "smart" devices such as locks, refrigerators, thermostats, light bulbs, vehicle, or your DVR and TV.  Newer, more obvious examples include items such as an Amazon Alexa.</p>

<h3>Security Concerns</h3>
<p>Our main concern when discussing IoT devices is always security, and for good reason.  With so many devices in our home or workplace, with such control over our safety and privacy, security is a major concern.</p>

<p>Our friend Ryan Wilson gave an excellent talk about IoT Device Security recently at a Vancouver tech conference, <a href="http://www.slideshare.net/RyanWilson65/ryan-wilson-ryanwilsoncom-iot-security?platform=hootsuite">you can read the presentation slides here</a>.</p>]]></content><author><name>Chris</name></author><category term="IoT" /><summary type="html"><![CDATA[What is the "Internet of Things" Buzzwords are super popular in tech circles, but keeping track of the latest trends and jargon is what we do best!]]></summary></entry><entry><title type="html">What are Standup Meetings?</title><link href="https://www.madebylotus.com/blog/what-are-standup-meetings/" rel="alternate" type="text/html" title="What are Standup Meetings?" /><published>2017-03-02T00:00:00+00:00</published><updated>2017-03-02T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/what-are-standup-meetings</id><content type="html" xml:base="https://www.madebylotus.com/blog/what-are-standup-meetings/"><![CDATA[<p>Everyone has been in a meeting or stand-up but here is how to make them better and how to get the most out of them. Time spent wisely is money spent wisely.</p>

<p>Here are some of those tips or rules to improve your stand-up:</p>

<ul><li>Have them at the same time in increments that make sense for the project, ex everyday, every week, every other week.</li><li>Keep them stand-up sort and to the point.</li><li>Include all the team members and make sure everyone understands to be on time.</li><li>Make sure to do your research and get your notes together before hand. This allows for you to be straight to the point and avoid getting lost in the details.</li><li>Have a person lead the meeting and another to document any decisions made so everyone says on the same page.</li><li>Have a quick question session at the end so everyone can make sure they are all on the same page and no new questions arose during the stand-up</li><li>Last, make sure you go over these tip/rules so there is structure and discipline to the stand-ups so they make the best use of everyone's time.</li></ul>

<p><img width="1360" height="425" src="/static/images/posts/what-are-standup-meetings/standup-icons.2e3908e799.webp" class="img-responsive" alt="Stand-up rules: same time, whole team, keep it short, have questions prepared, have someone lead the stand-up, keep notes"></p>

<p><em>Fun Fact: Stand-ups are called that because people were made to stand and give updates so they would keep their points short.</em></p>

<p>As far as coming to the stand-up prepared there are a few tricks to that too.</p>

<ul><li>Talk about what was done from the last stand-up till now.</li><li>Talk about what you plan to do before the next stand-up</li><li>Present any questions or blocks that you have to the group (talk to a person directly if you can to avoid getting off topic).</li><li>Say who goes after you so there is no lag in figure out how goes next.</li><li>If you are a lead and you feel like something is dragging on stop the discussion and suggest a separate meeting to talk about the problem in full detail.</li></ul>

<p>Conclusion</p>

<p>Make sure your teams knows the rules and what their roles is. Stay organized and on task and your stand-ups should improve making your teams more efficient.</p>

<h3>Have an Idea? We help make ideas blossom.</h3>]]></content><author><name>Chris</name></author><category term="entrepreneurship" /><summary type="html"><![CDATA[Everyone has been in a meeting or stand-up but here is how to make them better and how to get the most out of them. Time spent wisely is money spent wisely.]]></summary></entry><entry><title type="html">How to Use Sidekiq without Rails</title><link href="https://www.madebylotus.com/blog/use-sidekiq-without-rails/" rel="alternate" type="text/html" title="How to Use Sidekiq without Rails" /><published>2017-02-28T00:00:00+00:00</published><updated>2017-02-28T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/use-sidekiq-without-rails</id><content type="html" xml:base="https://www.madebylotus.com/blog/use-sidekiq-without-rails/"><![CDATA[<p>In a recent project, we found ourselves in a situation where it made sense to introduce a background processor to the project while working on an embedded device, but we weren’t using Rails.  To use sidekiq without Rails, we needed to solve simple issues such as startup and process supervision, so that we could reliably ensure Sidekiq would be running in our production environment.</p>

<p>We needed a processor for the following reasons:
* We interacted with services via the internet
* We expected failures and wanted the ability to retry easily
* We were processing large amount of incoming data from connected hardware and wanted to offload that to the background</p>

<h2>Installation without Rails</h2>
<p>Installing Sidekiq in a plain ruby project using Bundler wasn’t anything extraordinary — you simply add the gem to your Gemfile, bundle and define your workers.</p>

<p>Running the actual Sidekiq process required passing several options to specify the root of our project, how to load our code, the path to our configuration file and more.</p>

<pre><code>bundle exec sidekiq -r ./config/boot.rb -C ./config/sidekiq.yml
</code></pre>

<p>In our codebase, we had created a <code>config/boot.rb</code> to load bundler and require all of our classes to bootstrap our project. This was inspired by many other frameworks such as Sinatra or Rails. The <code>config/sidekiq.yml</code> file is standard, and since we had a config directory in our codebase, we shoved it in there to match Rails conventions.</p>

<h2>Running Sidekiq via SystemD</h2>
<p>Running locally in development is fine with the above command, but once deployed we needed a supervisor to ensure that Sidekiq was always running, and a way to start the Sidekiq process on boot. The answer to both questions is the wonderful <a href="https://www.freedesktop.org/wiki/Software/systemd/">SystemD</a>.</p>

<p><strong>You can read more about configuring ruby scripts, such as Sidekiq, in another post titled: "<a href="/blog/manage-ruby-processes-using-systemd-at-launch-on-linux/">How to Managing Ruby Processes at Launch</a>".</strong></p>

<p>Our SystemD service unit definition looks like the following:</p>

<script src="https://gist.github.com/macfanatic/2897888af5dea4408f2929c5575e442b.js"></script>

<p>You’ll notice that we are set to reset within a second if Sidekiq crashes for any reason, and that we want to wait until after the redis-server (also managed via SystemD) is available before starting.</p>

<h2>Daemons</h2>
<p>Don’t daemonize! There is a “fork” type in SystemD that would have required us to specify a PID file location and allow starting the “sidekiq” executable with the “-D” flag to run as a daemon, but there’s no need for it.</p>

<p>SystemD will start and monitor the Sidekiq process itself, and supervise to make sure it’s running. Instead of worrying about log files, we’re sending all output to <a href="https://en.wikipedia.org/wiki/Syslog">syslog</a>, a centralized logging service.</p>

<h2>Managing Sidekiq</h2>
<p>With the service fine enabled via SystemD, restarting or stopping Sidekiq has become very easy:</p>

<pre><code>sudo systemctl restart sidekiq
sudo systemctl status sidekiq
</code></pre>

<p>Sidekiq will launch during boot after redis-server is available, and automatically restart if it crashes. Awesome!</p>

<h2>Logging</h2>
<p>We hinted at logging with syslog already, but to view the logs you can use the journalctl program.</p>

<p>To view all entries from Sidekiq (even across reboots).</p>

<pre><code>sudo journalctl -u sidekiq
</code></pre>

<p>Or to just view messages from the most recent boot:</p>

<pre><code>sudo journalctl -u sidekiq -b
</code></pre>

<p>You can even pass options to filter to a specific time window, or do a live tail and grep to search for an occurrence of an error(great for debugging).</p>

<pre><code>sudo journalctl -u sidekiq -f | grep SomeErrorMessageToExpect
</code></pre>]]></content><author><name>Matt</name></author><category term="ruby" /><category term="raspberry-pi" /><category term="programming" /><category term="IoT" /><summary type="html"><![CDATA[In a recent project, we found ourselves in a situation where it made sense to introduce a background processor to the project while working on an embedded device, but we weren’t using Rails. To use sidekiq without Rails, we needed to solve simple issues such as startup and process supervision, so that we could reliably ensure Sidekiq would be running in our production environment.]]></summary></entry><entry><title type="html">How to use SSH Port Forwarding in Ruby HTTP Library</title><link href="https://www.madebylotus.com/blog/handling-port-forwarding-in-ruby/" rel="alternate" type="text/html" title="How to use SSH Port Forwarding in Ruby HTTP Library" /><published>2017-02-23T00:00:00+00:00</published><updated>2017-02-23T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/handling-port-forwarding-in-ruby</id><content type="html" xml:base="https://www.madebylotus.com/blog/handling-port-forwarding-in-ruby/"><![CDATA[<p>Sometimes you need to connect to a restricted server, by tunneling through a server that does have access — from your local machine. SSH tunneling and port forwarding are common ways to accomplish this.</p>

<p>Most people encounter the need to worry about port forwarding when attempting to connect to a database on a restricted host. This could be from a cloud provider, such as Amazon RDS or Microsoft Azure. Normally you’re web server will be granted access to connect to the database, but all other internet traffic will be restricted. To connect to the database, you need to seem as if you’re on the approved web server, so we’re going to “tunnel” through.</p>

<p>In this post, we’ll take a quick look at how you would accomplish this at the command line, and also entirely in ruby (and rails).</p>

<h2>SSH Port Forwarding at the Terminal</h2>
<p>Port forwarding can be accomplished at the terminal with a few additional options to the “ssh” command. You’ll need the following information to connect:</p>

<ul>
  <li>username and password to the database instance (target)</li>
  <li>port for the database instance (such as 3306 for MySQL or 5432 for PG)</li>
  <li>username and either a password, or SSH key to the web server (tunnel)</li>
  <li>a port you’d like exposed on your local machine (convention is 3307 or 5433 for instance)</li>
</ul>

<p>First you’ll want to just ensure that you can connect to the tunnel computer directly with a simple SSH command, such as:</p>

<pre><code>ssh -l username hostname -i certificate.pem
</code></pre>

<p>Assuming that your connection credentials are correct, we can worry about setting up a tunnel to your local machine now.</p>

<pre><code>ssh -L [local port]:[db hostname]:[db port] [tunnel username]@[tunnel hostname] -i certificate.pem
</code></pre>

<p>For a concrete example connecting to a MySQL instance:</p>

<pre><code>ssh -L [local port]:[db hostname]:[db port] [tunnel username]@[tunnel hostname] -i certificate.pem
</code></pre>

<p>If you see a SSH prompt, you should now be able to connect to your db instance via ‘localhost’ with your db user/pass, but use port 3307 instead of 3306 (or whatever you chose).</p>

<pre><code>ssl -L 3307:aws-rds.com:3306 matt@my-vps.com -i certificate.pem
</code></pre>

<p>That’s all great, but for a ruby project I might not want to have to remember to run a command before anything else, to forward a port locally. I’d like it to just work - hence we'll use port forwarding in ruby itself.</p>

<pre><code>require 'net/ssh/gateway'
Net::SSH::Gateway.new('my-vps.com', 'matt', keys: ['certificate.pem']).open('aws-rds.com', 3306, 3307)
</code></pre>

<p>To achieve that goal, we’ll make use of the net-ssh-gateway gem. You’ll need all the same information as above, but we’ll actually open our port and forward inside the ruby process itself.</p>

<p>With that snippet in place, we’ve opened port 3307 locally and can now connect to the database, without running any other commands on our machine. If we stop this ruby process, then the tunnel is terminated as well.</p>

<h2>Port Forwarding with Rails</h2>
<p>I’m going to answer the question most will ask — can this be used with Rails. While I haven’t tested, this should work wonderfully with rails.</p>

<p>I know you’re using Bundler instead of installing gems globally, so there’s no need to require anything. I’d place the snippet to open the connection in an initializer, and probably wrap in an environment check like so:</p>

<pre><code># config/initializers/production_ssh_tunnel.rb
Net::SSH::Gateway.new('my-vps.com', 'matt', keys: ['certificate.pem']).open('aws-rds.com', 3306, 3307)
</code></pre>

<p>Setting up “database.yml” connection credentials becomes trivial:</p>

<pre><code>production:
  adapter: mysql2
  username: db_user
  password: db_password
  database: db_name
  host: 3307
</code></pre>

<p>That’s all there is to it — ActiveRecord will connect to localhost port 3307, which is forwarded to your restricted server on the internet somewhere, and you’re in business.</p>

<p>I’d never recommend actually using this in a codebase as there is simply too high a chance for something to go wrong in a test, mistyped command, or at a console and poof you realize all too late you were operating against production data. Not to mention the version of your Rails app in memory (think a feature branch) could be vastly different than what is deployed on production server. You’ve been warned - it's dangerous to perform port forwarding in ruby in a production environment.</p>

<p>However, this could be a nifty way to code a migration script. Keep that thought, because in our next post we’ll explore how to write a ruby app with models and services that connects to 2 separate databases (PG and MySQL) to migrate data from one legacy system into another newer one.</p>]]></content><author><name>Matt</name></author><category term="networking" /><category term="linux" /><category term="ruby-on-rails" /><summary type="html"><![CDATA[Sometimes you need to connect to a restricted server, by tunneling through a server that does have access — from your local machine. SSH tunneling and port forwarding are common ways to accomplish this.]]></summary></entry><entry><title type="html">How to Design a Mobile App</title><link href="https://www.madebylotus.com/blog/how-to-design-a-mobile-app/" rel="alternate" type="text/html" title="How to Design a Mobile App" /><published>2017-02-21T00:00:00+00:00</published><updated>2017-02-21T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/how-to-design-a-mobile-app</id><content type="html" xml:base="https://www.madebylotus.com/blog/how-to-design-a-mobile-app/"><![CDATA[<h2>User Experience</h2>
<p>When starting the mobile app (UI/UX) designs make sure you keep the user in mind. The whole point of all this is to make the users life easier or benefit them by using the app. Making the registration process streamlined or keeping things out of sight they won't use often are a few examples of that.</p>

<p>Adding time or complications to the user aren't things designers or developers try to do. Get others that aren't working on the design to see if the work flow makes sense or if it seems overly complex. Sometimes the solution is by making the process longer, but simplified overall. Going back to the registration example. Most newer apps ask you one question at a time when creating a profile. This keeps the user on task and doesn't overwhelm them. This allows for them to simply answer the question and move on. The user feels like it is a speedy process and makes it easy.</p>

<p><img width="1360" height="425" src="/static/images/posts/how-to-design-a-mobile-app/examples.c57de47386.webp" class="img-responsive" alt="Four sign-up screens for the First McKinney Baptist Church app, asking one question at a time: email, password, then name" /></p>

<p>I stress this at the beginning because there are so many apps that designed because the app needs to do x, y, and z and it wasn't thought about how natural this is going to be for the user.</p>

<h2>Homework</h2>
<p>Going into your design make sure that you look into trends and competitors. You want to stand out from the competitors but be on the forefront of the design trends.</p>

<p><img width="800" height="600" src="/static/images/posts/how-to-design-a-mobile-app/voice_memos.f2c5358fb2.webp" class="img-responsive" alt="A voice memo app design: a pink recording screen with a waveform, beside a list of colour-coded recordings" /></p>

<h2>Designing</h2>

<h3>Details</h3>
<p>The details are extremely important. How a button works or is placed can really effect how efficiently the app is being used. Navigation is the key component in this. It is going to get users around the app. Typography is a necessity in this as you have limited space in your layout and need the type to be user friendly. The color scheme, this seems odd to pre pick out color but helps you limit your colors in your design. This allows you for you to see how the colors work together as well.</p>

<p><img width="1360" height="425" src="/static/images/posts/how-to-design-a-mobile-app/details.bbcadd238e.webp" class="img-responsive" alt="Two app screens, a navigation menu and an event with a map, beside the app's five-colour palette" /></p>

<h3>Testing</h3>
<p>Quality control is a must. You want to make sure your app works and in the way you intended. This is also you chances to make sure it works well for the user. Checking to make sure buttons are in reach of your fingers of your right hand. Most people use their phone one handed. Also try to check and make sure you app looks good on older models. You want everyone to have the same great experience.</p>

<p><img width="700" height="219" src="/static/images/posts/how-to-design-a-mobile-app/thumb_zone.0596085892.webp" class="img-responsive" alt="Diagram of the thumb zone on a phone held upright and sideways, marking the ideal placement for primary navigation" /></p>

<h2>Closing</h2>
<p>Some final advice, make sure you work closely with your developers throughout this process. You want to make sure your vision can me created and they know how to. You don't want to create this amazing idea and have no way for it to be created.</p>]]></content><author><name>Chris</name></author><category term="design" /><summary type="html"><![CDATA[User Experience When starting the mobile app (UI/UX) designs make sure you keep the user in mind. The whole point of all this is to make the users life easier or benefit them by using the app. Making the registration process streamlined or keeping things out of sight they won't use often are a few examples of that.]]></summary></entry><entry><title type="html">How to Test ActiveJob Callbacks Using Sidekiq</title><link href="https://www.madebylotus.com/blog/how-to-test-activejob-callbacks-using-sidekiq/" rel="alternate" type="text/html" title="How to Test ActiveJob Callbacks Using Sidekiq" /><published>2017-02-16T00:00:00+00:00</published><updated>2017-02-16T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/how-to-test-activejob-callbacks-using-sidekiq</id><content type="html" xml:base="https://www.madebylotus.com/blog/how-to-test-activejob-callbacks-using-sidekiq/"><![CDATA[<p>While I have used Sidekiq for years now, I’m also a big fan of ActiveJob. I still use vanilla Sidekiq workers for some tasks, where I require many of the advanced features of Sidekiq that aren’t available in ActiveJob, but ActiveJob workers are just great for a vast majority of tasks.</p>

<p>In this post, we’ll take a quick look at how you can test ActiveJob callbacks and error handling in your jobs.</p>

<h2>Our Worker</h2>
<p>Let’s first define a simple worker that raises an exception, that we expect to handle by logging with the Rails logger.</p>

<script src="https://gist.github.com/macfanatic/caef6d3ae8988a951ff5b1b2ee984db3.js"></script>

<p>This isn’t a very exciting worker, but it does show that when performed we will immediately raise a simple exception, which we expect to be handled gracefully by logging to the console.</p>

<h2>First Attempt at Testing</h2>
<p>I would normally test this out by mocking the API communication layer in a unit test, so my spec would look like the following:</p>

<script src="https://gist.github.com/macfanatic/2c89b164bae21584d8a066434b8aa5fc.js"></script>

<p>While this looks perfectly reasonable, and we’re testing the “perform” method of our worker just fine, this test will actually fail. It has to do with how ActiveJob is performing our worker.</p>

<p><a href="http://api.rubyonrails.org/classes/ActiveJob/Execution.html#method-i-perform_now">ActiveJob: Execution Documentation</a></p>

<p>Looking through the documentation and source for “perform” vs “perform<em>now”, it’s apparent that “perform</em>now” is actually required to run callbacks and handle errors. So we need to update our test to reflect that:</p>

<pre><code>subject.perform =&gt; subject.perform_now
</code></pre>

<p>With that change, our test will pass!</p>

<h2>Simplest Test Case</h2>
<p>While that change does get the test passing, we can refactor our test to be even simpler to relay what is being tested here.</p>

<script src="https://gist.github.com/macfanatic/d94021f4fa99af4c1240a69ef230e292.js"></script>

<p>I’ve now removed any necessary knowledge of using the API client itself, and just asserted that if the “perform” method raises this error, presumably b/c we’ve called the API client and it was not handled there, that all is well.</p>

<p>If you need help running Sidekiq (with or without Rails) on startup, take a look at our post "<a href="/blog/manage-ruby-processes-using-systemd-at-launch-on-linux/">How to Manage Ruby Processes at Launch</a>".</p>]]></content><author><name>Matt</name></author><category term="ruby" /><category term="ruby-on-rails" /><summary type="html"><![CDATA[While I have used Sidekiq for years now, I’m also a big fan of ActiveJob. I still use vanilla Sidekiq workers for some tasks, where I require many of the advanced features of Sidekiq that aren’t available in ActiveJob, but ActiveJob workers are just great for a vast majority of tasks.]]></summary></entry><entry><title type="html">The Design Process</title><link href="https://www.madebylotus.com/blog/the-design-process/" rel="alternate" type="text/html" title="The Design Process" /><published>2017-02-14T00:00:00+00:00</published><updated>2017-02-14T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/the-design-process</id><content type="html" xml:base="https://www.madebylotus.com/blog/the-design-process/"><![CDATA[<p>The design process is a little different for each design. No matter what you are designing though it is usually is never a straight path, there are always forks and turns that lead you to that ideal solution and final product.</p>

<p><img src="/static/images/posts/the-design-process/designprocess-01.5903cc06c2.webp" class="img-responsive" width="160" height="160" style="width: 160px;" alt=""></p>

<h2>Defining The Problem</h2>

<p>Defining the problem is one of the most important steps. Why? Identify the problem from all aspects and how it fully affects the user is key in finding a solution that fully solves the problem by addressing all aspects.</p>

<p><img src="/static/images/posts/the-design-process/designprocess-02.6e1d95e2cf.webp" class="img-responsive" width="160" height="160" style="width: 160px;" alt=""></p>

<h2>Research</h2>

<p>Research is key to finding a solution that not only solves the defined problem but does not create new problems. Also researching to make sure it isn’t a solution that has been patented or previously created.</p>

<p><img src="/static/images/posts/the-design-process/designprocess-03.8751269ab0.webp" class="img-responsive" width="160" height="160" style="width: 160px;" alt=""></p>

<h2>Brainstorm (Incubation)</h2>

<p>Creating a solution is not always as simple as, we did the research and here is the plan. Great solutions come at the right moment. One thing most people don’t think about is Incubation, which is a key component of design work. Incubation is where you start thinking about a problem and start creating solutions and possibly solving it subconsciously. (Ex: That shower thought you have to solve a problem is because of the incubation process.) Sometimes it is quick and other times it takes a while while to create a solution that really solves the problem.</p>

<p><img src="/static/images/posts/the-design-process/designprocess-04.208ba658f3.webp" class="img-responsive" width="160" height="160" style="width: 160px;" alt=""></p>

<h2>Execution</h2>

<p>The construction of the idea is a test of the theoretical solution. This trial and error irons out the problems and all possible hiccups. This is key and stressful time as it validates the solution or puts you back a square one.</p>

<p><img src="/static/images/posts/the-design-process/designprocess-05.07840d6a81.webp" class="img-responsive" width="160" height="160" style="width: 160px;" alt=""></p>

<h2>Critics/Feedback</h2>

<p>Feedback is key to the success of design process. At this point if the same people have been working on the same project the need for a new perspective to step in an point out any flaws or other possibilities is high. Getting customer feedback also helps to make sure the project is on track before it is too late. Making sure that the designs match what the client had in their mind and works as they thought or better is key to the product's success.</p>

<p><img src="/static/images/posts/the-design-process/designprocess-06.dc4eb75d56.webp" class="img-responsive" width="160" height="160" style="width: 160px;" alt=""></p>

<h2>Improve/Final design</h2>

<p>Taking all of this, modeling everything together into one experience and finishing the project. All of these steps together gets you here to the final product.</p>]]></content><author><name>Chris</name></author><category term="branding" /><category term="design" /><category term="ui-ux" /><summary type="html"><![CDATA[The design process is a little different for each design. No matter what you are designing though it is usually is never a straight path, there are always forks and turns that lead you to that ideal solution and final product.]]></summary></entry><entry><title type="html">How to Manage Environment Variable in Ruby Subprocess</title><link href="https://www.madebylotus.com/blog/manage-environment-variable-in-ruby/" rel="alternate" type="text/html" title="How to Manage Environment Variable in Ruby Subprocess" /><published>2017-02-09T00:00:00+00:00</published><updated>2017-02-09T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/manage-environment-variable-in-ruby</id><content type="html" xml:base="https://www.madebylotus.com/blog/manage-environment-variable-in-ruby/"><![CDATA[<p>I recently worked on a project that caused some puzzlement when spawning subprocesses multiple times, all while continuing to use bundler to manage our gems.  I needed to find an easy way to manage environment variable in ruby using bundler, NodeJS and rbenv.</p>

<h2>The Problem</h2>
<p>We have a custom node app that runs acting as a coordinator to executed arbitrary tasks (most of them written in ruby) to do things such as generate screenshots for an iOS application.</p>

<p>The node app itself contains the ruby scripts in the same directory, which uses Bundler to manage gems. However, when node executes a task it does that in a unique folder in the system tmp directory to execute many tasks at a time on different data sets.</p>

<p>Ultimately we wanted to use rbenv and bundler to manage ruby and gems, but yet needed that to work from any arbitrary path on the filesystem (outside the directory containing the ruby code and the Gemfile).</p>

<h2>Example Filesystem</h2>
<p>Here’s an example situation that illustrates the issue:</p>

<pre><code>- ~/Documents/node_app/
  - .ruby-version
  - app.js
  - Gemfile
  - tasks/
    - custom_script.rb
- /tmp/
  - unique_task_run
</code></pre>

<p>In the above setup, the “app.js” defines the running NodeJS app which would execute “custom_script.rb” via a subprocess like the following:</p>

<pre><code>var exec = Promise.promisify(require('child_process').exec)
return exec("custom_script.rb", {
  env: extend({
    CUSTOM_ENV: "value"
  }, process.env)
});
</code></pre>

<p>In the above code, we configure the environment variables to pass to the ruby script by including everything currently in our environment along with an extra value. This executes fine, until we execute a task from within the ruby script, such as:</p>

<pre><code>#!/usr/bin/env ruby
Dir.chdir('/tmp') do
  Dir.chdir('some_new_folder')
  result = `executable_from_gemfile`
end
</code></pre>

<h2>Ruby ENV</h2>
<p>Our ruby script has environment variables set from the running Node process, which is great. The current script is even located within the project, so there is a Gemfile nearby. However, when we execute a new script inside our ruby script, we’re no longer in the node directory — we’re in the “tmp” directory working on a dataset.</p>

<p>There are many different ways of executing tasks in ruby, and we’ll be writing another post on that very subject later on. For now, let’s just review the documentation to know that using backticks executes the task in a new shell and passes along everything in the current ENV to the new task.</p>

<p>Observant readers would have undoubtably already noticed that we’re not calling our executable using bundler. So let’s try this:</p>

<pre><code>result = `bundle exec executable_from_gemfile`
</code></pre>

<p>While you would think this would work, we receive an error because bundler cannot find our Gemfile.</p>

<pre><code>Could not locate Gemfile or .bundle/ directory
</code></pre>

<h2>Bundler Gemfile Environment Variable</h2>
<p>We’re going to solve this problem by working back up the call tree of processes. First off, let’s observe that changing our ruby script to the following fixes our issue:</p>

<pre><code>result = `BUNDLE_GEMFILE=~/Documents/node_app/Gemfile bundle exec executable_from_gemfile`
</code></pre>

<p>By setting this environment variable, bundler can locate the Gemfile and run the executable provided by a gem in our bundle just fine. However, it’s cumbersome to provide this info every time and error prone.</p>

<p>Refactoring to introduce a method to prefix the environment to a command would be a good step in the right direction, but feels a little clunky.</p>

<h2>Remember, ENV is inherited</h2>
<p>If we recall that the ENV is inherited for a subprocess, then ensuring that the parent process executing our “custom_script.rb” provides us with this information is the next step. In our example, the node process is our parent.</p>

<pre><code># node
exec("custom_script.rb", {
  env: extend({
    "BUNDLE_GEMFILE": "~/Documents/node_app/Gemfile"
   }, process.env)
})
</code></pre>

<p>By specifying the environment variable when we execute the ruby script from node, we’ve ensured that any tasks executed can in turn run a subprocess of their own and use “bundle exec” properly, no matter the current directory the task is running from.</p>

<h3>One More Level</h3>
<p>Setting the “BUNDLE_GEMFILE” setting in our node codebase isn’t good practice either, and could vary by installation if we deploy the same node app to different servers. One easy way to define this would be an environment variable when starting the node app. We were already using the wonderful dotenv package, so adding this was easy.</p>

<pre><code># .env
BUNDLE_GEMFILE=~/Documents/node_app/Gemfile
</code></pre>

<h2>Wrapping Up</h2>
<p>Our final solution boils down to this:</p>

<pre><code># .env
BUNDLE_GEMFILE=~/Documents/node_app/Gemfile
# app.js
exec("custom_script.rb", {
  env: extend({
    CUSTOM_VAR: "value"
  }, process.env)
})
# custom_script.rb
result = `bundle exec executable_from_gemfile`
</code></pre>

<p>We’ve ensured that any subprocesses spawned by ruby scripts, executed from our node app, properly inherit environment variables and can use “bundle exec” with ease. Additionally, since we’re using the “shims” feature of rbenv, the “bundle” command itself is properly linked to our version of ruby no matter the directory we’re executing from.</p>]]></content><author><name>Matt</name></author><category term="linux" /><category term="ruby-on-rails" /><category term="programming" /><summary type="html"><![CDATA[I recently worked on a project that caused some puzzlement when spawning subprocesses multiple times, all while continuing to use bundler to manage our gems. I needed to find an easy way to manage environment variable in ruby using bundler, NodeJS and rbenv.]]></summary></entry><entry><title type="html">Logo Design Basics</title><link href="https://www.madebylotus.com/blog/logo-design-basics/" rel="alternate" type="text/html" title="Logo Design Basics" /><published>2017-02-07T00:00:00+00:00</published><updated>2017-02-07T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/logo-design-basics</id><content type="html" xml:base="https://www.madebylotus.com/blog/logo-design-basics/"><![CDATA[<h3>Branding</h3>

<blockquote><em>“The process involved in creating a unique name and image for a product in the consumers’ mind, mainly through advertising campaigns with a consistent theme. Branding aims to establish a significant and differentiated presence in the market that attracts and retains loyal customers.”</em></blockquote>

<p>In this case theme being what I will refer to as style.</p>

<h3>Logo</h3>

<blockquote><em>“Recognizable and distinctive graphic design, stylized name, unique symbol, or other device for identifying an organization. It is affixed, included, or printed on all advertising, buildings, communications, literature, products, stationery, and vehicles. Not to be confused with a brand, which identifies a product or family of products”.</em></blockquote>

<h3>Logo Design Basics</h3>

<h4>-Text Logo</h4>

<p>Using typography in a unique way to create your identity.</p>

<p><img src="/static/images/posts/logo-design-basics/text-logo-google.694789c564.webp" class="img-responsive" width="640" height="351" alt="The Google logo, an example of a text logo"></p>

<h4>-Symbol Logo</h4>

<p>Using a symbol or image to create your company identity.</p>

<p><img src="/static/images/posts/logo-design-basics/symbol-logo-starbucks.20af349357.webp" class="img-responsive" width="320" height="322" style="width: 320px;" alt="The Starbucks siren, an example of a symbol logo"></p>

<h4>-Mixed Logo</h4>

<p>Using a both a symbol and text to create an identity were they can work together or apart.</p>

<p><img src="/static/images/posts/logo-design-basics/mixed-logo-pepsi.b7c55d4029.webp" class="img-responsive" width="640" height="256" alt="The Pepsi globe and wordmark, an example of a mixed logo"></p>

<h3>Theme</h3>

<blockquote><em>“Central, recurring idea or message that connects individual ads (comprising an advertising campaign) to produce a cumulative effect far greater than the total effect of the individual, disconnected ads.”</em></blockquote>

<h3>Brand Identity</h3>

<blockquote><em>“Characteristics or elements combined and expressed in a particular (often unique) and consistent manner.” </em></blockquote>

<figure><img width="1360" height="907" src="/static/images/posts/logo-design-basics/brand-identity-bistro.3eb9901bb6.webp" class="img-responsive" alt="A bistro's brand shown on a tablet, business cards, thank-you cards and a mug"><figcaption>Ex: Artistic, Unique</figcaption></figure>]]></content><author><name>Chris</name></author><category term="marketing" /><category term="typography" /><category term="branding" /><category term="design" /><summary type="html"><![CDATA[Branding]]></summary></entry><entry><title type="html">What the font?</title><link href="https://www.madebylotus.com/blog/what-the-font/" rel="alternate" type="text/html" title="What the font?" /><published>2017-02-03T00:00:00+00:00</published><updated>2017-02-03T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/what-the-font</id><content type="html" xml:base="https://www.madebylotus.com/blog/what-the-font/"><![CDATA[<h3>Typography</h3>

<p>ty·pog·ra·phy<br><em>noun</em><br>the style and appearance of printed matter.<br>the art or procedure of arranging type or processing data and printing from it.</p>

<p>Typography is the art of visually laying out text that appeals to the viewer and brings them in. It can be readable or not. It can make a big difference in how someone visually perceives a website or a document. It can make something harder or easier to read.</p>

<figure><img width="1360" height="279" src="/static/images/posts/what-the-font/typeface.c484602bc5.webp" class="img-responsive" alt="A typeface broken down into font families, font styles and font sizes, ending with Myriad Pro Regular 50"><figcaption><em>A typeface is made of several different elements. Font Family, Font Style, Font Size.</em></figcaption></figure>

<p>So what is a font? it is the final Myraid Pro Regular 50. It is whole the text is going to look. Learning about typography and its many different aspects can be confusing. There are typefaces, font families, and just fonts. These all mean something a little different. There is a whole library of terms breaking down each part of the letter.</p>

<figure><img width="1360" height="279" src="/static/images/posts/what-the-font/type-anatomy.1ec748c5ea.webp" class="img-responsive" alt="The word Glossary labelled with the parts of a letter: ascender, spine, beak, shoulder, serif, bracket, axis, counter and tail"></figure>

<p>You don’t have to know every term to become great with typography. Having a good eye and understanding some basic understanding of design rules can go far.</p>

<p>Here are some of the rules of typography</p>

<p><strong>1. Size</strong><br>Typefaces are not all the same. Some are wider or bolder than others. This means that depending on the size some take up more or less room even being the same font size.</p>

<p><strong>2. Leading</strong><br>Leading is the space between the lines. In school you would double space a paper or paragraph. That would be changing your leading value to 2.0. This helps in list views and areas were you need your type to take up more room but don’t need it bigger. You can add some space between the times and it will help make it easier to read as well.</p>

<p><strong>3. Tracking and kerning</strong><br>Kerning helps just make everything look perfect. It is super important on titles and bigger text items were you can see the space between the letters.</p>

<blockquote><em>"Kerning describes the act of adjusting the space between characters to create a harmonious pairing. For example, where an uppercase ‘A’ meets an uppercase ‘V’, their diagonal strokes are usually kerned so that the top left of the ‘V’ sits above the bottom right of the ‘A’.Kerning similar to, but not the same as, ‘tracking’; this relates to the spacing of all characters and is applied evenly."</em> - <a href="http://www.creativebloq.com/typography/what-is-typography-123652">Creative Blog</a></blockquote>

<p><strong>4. Measure</strong><br>Measure meaning to consider the width of the text block. Some times having something very wide in a smaller font can be hard to red. Consider breaking it into columns.</p>

<p><strong>5. Hierarchy</strong><br>If all the type was the same color and size we wouldn’t know what is important. What is the title and who wrote it. We need hierarchy to help layout to the user what is important and what they need to remember. To do this we can use color, spacing, size, placement and weight.</p>

<p>Using these basic rules will allow you create documents to pull and hook users. Doing this will removes any jarring conflicts or strain to the user. Also being able to identify the parts of a font and see the differences to know which font is what. If you have trouble you can use <a href="https://www.myfonts.com/WhatTheFont/">What The Font</a> a tool that can identify a font from a photo.</p>

<p>See How to Analyze a Companies Brand Identity to see how to incorporate your font choices into your Brand Identity.</p>]]></content><author><name>Chris</name></author><category term="typography" /><category term="design" /><summary type="html"><![CDATA[Typography]]></summary></entry><entry><title type="html">UI/UX Design Overview</title><link href="https://www.madebylotus.com/blog/uiux-design-overview/" rel="alternate" type="text/html" title="UI/UX Design Overview" /><published>2017-02-02T00:00:00+00:00</published><updated>2017-02-02T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/uiux-design-overview</id><content type="html" xml:base="https://www.madebylotus.com/blog/uiux-design-overview/"><![CDATA[<h3>History</h3>

<p>User interface design wasn’t always as we would think of it today. It started back in the 40’s with Batch UI. Computers punching holes in cards and creating commands. In the 60’s we had screens and used Command Line UI, similar to a terminal. Apple changed how all of this worked in 1984 with the Macintosh. Creating the point and click interface as we know it today. Since then we have been able to really design how a person is interacting with digital items.</p>

<p>In 2007 we made a major change with touch screens being integrated into mobile devices. With every change in product changed how we could interact with the digital assets. Each new milestone in the tech world has allowed new avenues for people to interact with their computer. The mouse, trackpad, stylus, touch-screens, virtual realty, all of these have changed the way were using are bodies to interact with our technology.</p>

<h3>Purpose</h3>

<p>The goal with UI/UX design is to create something that doesn’t seem new, it seems natural to the user. Whether if this using something that has been done for years and maybe repurposing it, or bring something in humans do naturally and giving it a digital input.</p>

<p>As we move into more of a digital world there are more hurdles to overcome. There are less physical pieces to help the user. This means that the symbols and animations that are used have to show and indicate in some way what the user should do.</p>

<p>Somethings are just learned over time such as the hamburger menu icon. It is something a company started to using and caught on as an industry trend. Now whenever anyone sees it we know to tap it and get a menu.</p>

<p><img src="/static/images/posts/uiux-design-overview/hamburger-menu.9b41b6512a.webp" class="img-responsive" width="369" height="656" style="width: 369px;" alt="An app's event calendar screen with the hamburger menu icon circled in the top left"><img src="/static/images/posts/uiux-design-overview/sidebar-menu.b4f3fb3ca5.webp" class="img-responsive" width="369" height="656" style="width: 369px;" alt="The same app with its sidebar menu open, listing Overview, Calendar, Media, Messaging and more"></p>

<h3>Design</h3>

<p>There are guidelines in place such as the <em>Principle of least astonishment</em>. This principle states that you should take the path of least surprising in how something works. You don’t want to catch the user off guard by how oddly a function works. You want to minimize the learning curve and use the users previous knowledge to your advantage.</p>

<p>There are eight characteristics to think about when designing for UI/UX:</p>

<p><strong>Familiarity:</strong> Ensuring that first time users can find things they are familiar with and branching out from there. In some cases these is keeping the branding and UI/UX the same across platfroms.<br><strong>Efficiency:</strong> Time is money. The user interface should be quickly learned or they should be able to easily access key or common tools.<br><strong>Consistency:</strong> Use templates and similar layouts. Don’t change where buttons are on different pages. Try to create a layout that works in almost every situation.<br>Aesthetics: Making something easy on the eyes makes people more relaxed and enjoy using the product more.<br><strong>Clarity:</strong> Making sure that the interface is simple, clear, and precise. You want the user to clearly see and understand what they need to do and use.<br><strong>Responsiveness:</strong> The interface needs to let the user know what is happening even if it is loading. So even if things are being slow the user can see that processes are happening and they can be reassured.<br><strong>Forgiveness:</strong> Giving ways to point out and easy correct mistakes that the users made.<br><strong>Conclusion:</strong> its easy to make things clear and organized but double checking and making sure that labels or complex menus aren’t creating more problems than they are solving or create more clutter.</p>

<p>Bringing Branding into your app allows for further and future develpment of other apps and other components to you company. Knowing what your company is about while designing you app is important. Check out our worksheet to help figure out who you company is.</p>

<p>With some of this basic knowledge you can start to break down UI/UX you never really thought about, understand how they did it. Start to analysis and integrrate some of that into your UI designs.</p>]]></content><author><name>Chris</name></author><category term="design" /><category term="ui-ux" /><summary type="html"><![CDATA[History]]></summary></entry><entry><title type="html">Understanding the basics of Branding through Brand Identity Examples</title><link href="https://www.madebylotus.com/blog/brand-identity-examples/" rel="alternate" type="text/html" title="Understanding the basics of Branding through Brand Identity Examples" /><published>2017-01-31T00:00:00+00:00</published><updated>2017-01-31T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/brand-identity-examples</id><content type="html" xml:base="https://www.madebylotus.com/blog/brand-identity-examples/"><![CDATA[<p>When thinking of how to brand your company there is a variety of factors that come into play.</p>

<p>Audience that you are marketing to is number one. Clearly defining the individuals or organizations that are going to be the ideal for your product or service is key. Market research, experience and instinct all play varying roles in this process and is key to starting off on the right foot.</p>

<h3>Brand Identity</h3>

<ul><li>The “feel” or emotion, depending on market. Ex: The medical industry has a very clean feel because you want the customer to feel clean when viewing your company.</li><li>Style can be included with emotion. Ex: A clean, yet modern style that feels fresh and not sterile.</li><li>Part of the persona is also how you want to feel as the emotion towards the company. Such as darker colors may seem closed off and secretive but also more modern. In contrast, lighter colors will make you seem more transparent but can also come off as more playful in some cases.</li></ul>

<p>All of this plays into the personality that you are creating for your company. Essentially the first impression when meeting someone, and essential to your brand’s message.</p>

<h3>Brand Identity Examples</h3>

<p>There are many different parts to your company. There is letter head, business cards, a website, mobile apps, advertising and so on. When beginning your branding journey, having an idea on what some of the assets you are going to want to create is a good idea. Some of the designs aren’t going to work across all media types and it is better to know ahead of time so it can be kept in mind throughout the design process. Additionally, some assets may provide different design avenues that are more desirable.</p>

<figure><img width="1000" height="680" src="/static/images/posts/brand-identity-examples/brand-identity-mockup.43806bc0ac.webp" class="img-responsive" alt="Mockup of one brand applied to stationery, a phone, a cup, an envelope, a book and a post card"><figcaption>Example assets. <a href="http://www.graphicsfuel.com/2015/11/cardboard-cutout-logo-mockup/">Source</a></figcaption></figure>

<h3>Press Kit</h3>

<p>Depending on your organization there are some things you should consider when creating marketing materials such as a Press kit. This provides other companies with information about your company andproduct that can be useful in marketing, like having an article written about your company. You can send your Press kit and they can have access to your logo, <a href="/blog/what-the-font/">fonts</a>, color scheme and so on. That way all the effort spent on branding can be shown and used no matter who is doing it.</p>

<p><img width="1275" height="1650" src="/static/images/posts/brand-identity-examples/prospector-press-kit.71e103b2e5.png" class="img-responsive" alt="The Prospector press kit: an About section, the logo on coloured and white backgrounds, the colour scheme, fonts and contact details"></p>

<h3>Conclusion</h3>

<p>Branding is all about showing the world what your company is about. Presenting the best side to the world while simply showing what you do and can do. This can be hard to do in an image or logo but simplifying what the company is about and focusing in on the main traits can make this process easier. This is why design and research is so important. You want to make sure you are telling the right story about the company.</p>]]></content><author><name>Chris</name></author><category term="marketing" /><category term="branding" /><category term="design" /><summary type="html"><![CDATA[When thinking of how to brand your company there is a variety of factors that come into play.]]></summary></entry><entry><title type="html">Hand Crafted Holiday Boxes</title><link href="https://www.madebylotus.com/blog/hand-crafted-holiday-boxes/" rel="alternate" type="text/html" title="Hand Crafted Holiday Boxes" /><published>2016-12-13T00:00:00+00:00</published><updated>2016-12-13T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/hand-crafted-holiday-boxes</id><content type="html" xml:base="https://www.madebylotus.com/blog/hand-crafted-holiday-boxes/"><![CDATA[<p><img width="1360" height="1051" src="/static/images/posts/hand-crafted-holiday-boxes/gift-box.7a5ece4334.webp" class="img-responsive" alt="Lotus Apps mugs filled with bags of custom candy, lined up on a table"></p>

<p>This year at Lotus we wanted to do something really special and unique for our clients. We wanted it to say thank you, whilst also giving a little insight of the company, something that showed who we are. We decided to craft gift boxes to show how we put thought and feeling into our work and ever detail of our design, marketing and development process.</p>

<p><img src="/static/images/posts/hand-crafted-holiday-boxes/mugs-and-candy.121b2ec927.webp" class="img-responsive" width="320" height="378" style="width: 320px;" alt="Close-up of Lotus Apps mugs filled with blue, green and white custom M&amp;Ms"></p>

<p>We had custom mugs and candy made for the enjoyment of our clients.</p>

<p><img src="/static/images/posts/hand-crafted-holiday-boxes/stamp.08a9adf617.webp" class="img-responsive" width="320" height="333" style="width: 320px;" alt="The wooden Lotus flower stamp used on the boxes and cards"></p>

<p>The boxes and cards were stamped.</p>

<p><img src="/static/images/posts/hand-crafted-holiday-boxes/stamped-card.2cb7518004.webp" class="img-responsive" width="320" height="320" style="width: 320px;" alt="A kraft paper card stamped with the Lotus flower in white"></p>

<p>Chad had the best handwriting and wrote with a calligraphy pen.</p>

<p><img src="/static/images/posts/hand-crafted-holiday-boxes/packed-box.97a978af6d.webp" class="img-responsive" width="320" height="427" style="width: 320px;" alt="An open gift box holding a stamped card, a business card and a Lotus Apps mug"></p>

<p>We laid it all out. Put in a few business cards and our newest clients even got their welcome T-Shirts.</p>

<p>We hope all of our clients enjoy their hand crafted holiday boxes. We would like to thank them for trusting us with building their businesses and crafting custom solutions to meet their needs. Here is to a wonderful 2017!</p>]]></content><author><name>Chris</name></author><category term="marketing" /><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Lotus Apps T-Shirts</title><link href="https://www.madebylotus.com/blog/lotus-apps-t-shirts/" rel="alternate" type="text/html" title="Lotus Apps T-Shirts" /><published>2016-08-16T00:00:00+00:00</published><updated>2016-08-16T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/lotus-apps-t-shirts</id><content type="html" xml:base="https://www.madebylotus.com/blog/lotus-apps-t-shirts/"><![CDATA[<p><img width="972" height="972" src="/static/images/posts/lotus-apps-t-shirts/t-shirts.9c08b4d167.webp" class="img-responsive" alt="Marcel wearing a red Lotus Apps T-shirt in front of a graffiti wall"></p>

<p>We love to give our friends and customers a little Lotus Apps love, and Marcel is rocking one of our T-Shirts we sent him.</p>

<p>If you’d like to show your support for Lotus, shoot us an email or ask on twitter and we’ll see what we can do. Of course, we’d love to see a picture of you enjoying these awesome shirts from our friends at <a href="http://www.riotprinting.com">Riot Printing</a>.</p>]]></content><author><name>Matt</name></author><category term="marketing" /><category term="branding" /><category term="design" /><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Managing Ruby Scripts at Launch</title><link href="https://www.madebylotus.com/blog/manage-ruby-processes-using-systemd-at-launch-on-linux/" rel="alternate" type="text/html" title="Managing Ruby Scripts at Launch" /><published>2016-06-07T00:00:00+00:00</published><updated>2016-06-07T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/manage-ruby-processes-using-systemd-at-launch-on-linux</id><content type="html" xml:base="https://www.madebylotus.com/blog/manage-ruby-processes-using-systemd-at-launch-on-linux/"><![CDATA[<p>For the past several months, we have been working on a software solution to run on embedded devices and recently found ourselves needing to solve the problem of how to run some of our software at determined times in the device lifecycle. In this post, we'll explore the solutions that we explored, why we didn't choose any of those solutions, and what we ended up with that has worked well.</p>

<p><img width="1360" height="907" src="/static/images/posts/manage-ruby-processes-using-systemd-at-launch-on-linux/motherboard.88d121c8ee.webp" class="img-responsive" alt="" /></p>

<h2>The Problem</h2>

<p>Our goal is deceptively simple - we need to ensure that our software runs when a device is powered on. I say deceptively simple because we need to ensure that other supporting services are running. We wanted to wait until we had an active internet connection for instance, and most of our software depends upon a local redis instance being booted. We decided to write our software in ruby, but documentation on IoT (Internet of Things) ruby development or running ruby on an embedded device is relatively scarce. The good news is Linux has us covered!</p>

<p>The Linux community at large has many competing solutions to this problem, and reliable documentation has been hard to find. In our specific case, we have a collection of services that need to run on boot, comprised of scripts and long-running ruby processes. Additionally, our deployment device is a Raspberry Pi running Rasbian Linux, so our solution must work there.</p>

<h2>Possible Solutions</h2>

<p>We researched a few different solutions to manage ruby processes, outlined below. Ultimately we utilized Systemd.</p>

<h3>System V (init.d) Script</h3>

<p>Research how to run a bash script on boot in Linux, and the answer will most likely be creating a script in the <code>/etc/init.d/</code> folder. The script itself is a bash script, which ends up being a lot of boilerplate code and very easy to get wrong. This is widely supported, although deprecated on most Linux distributions.</p>

<h4>Pros</h4>

<ul>
  <li>Already installed</li>
  <li>Widely supported across Linux distros</li>
</ul>

<h4>Cons</h4>

<ul>
  <li>Lots of boilerplate code</li>
  <li>Difficult to read and write</li>
  <li>No formal concept of load order to init scripts</li>
  <li>Deprecated in favor of newer tools such as Upstart, Runit, or Systemd</li>
  <li>Have to handle daemonization ourselves</li>
</ul>

<h4>Example</h4>

<pre><code>#!/bin/sh
### BEGIN INIT INFO
# Provides:
# Required-Start:    $remote_fs $syslog
# Required-Stop:     $remote_fs $syslog
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Start daemon at boot time
# Description:       Enable service provided by daemon.
### END INIT INFO

dir=""
cmd=""
user=""

name=`basename $0`
pid_file="/var/run/$name.pid"
stdout_log="/var/log/$name.log"
stderr_log="/var/log/$name.err"

get_pid() {
    cat "$pid_file"
}

is_running() {
    [ -f "$pid_file" ] &amp;&amp; ps `get_pid` &gt; /dev/null 2&gt;&amp;1
}

case "$1" in
    start)
    if is_running; then
        echo "Already started"
    else
        echo "Starting $name"
        cd "$dir"
        if [ -z "$user" ]; then
            sudo $cmd &gt;&gt; "$stdout_log" 2&gt;&gt; "$stderr_log" &amp;
        else
            sudo -u "$user" $cmd &gt;&gt; "$stdout_log" 2&gt;&gt; "$stderr_log" &amp;
        fi
        echo $! &gt; "$pid_file"
        if ! is_running; then
            echo "Unable to start, see $stdout_log and $stderr_log"
            exit 1
        fi
    fi
    ;;
    stop)
    if is_running; then
        echo -n "Stopping $name.."
        kill `get_pid`
        for i in {1..10}
        do
            if ! is_running; then
                break
            fi

            echo -n "."
            sleep 1
        done
        echo

        if is_running; then
            echo "Not stopped; may still be shutting down or shutdown may have failed"
            exit 1
        else
            echo "Stopped"
            if [ -f "$pid_file" ]; then
                rm "$pid_file"
            fi
        fi
    else
        echo "Not running"
    fi
    ;;
    restart)
    $0 stop
    if is_running; then
        echo "Unable to stop, will not attempt to start"
        exit 1
    fi
    $0 start
    ;;
    status)
    if is_running; then
        echo "Running"
    else
        echo "Stopped"
        exit 1
    fi
    ;;
    *)
    echo "Usage: $0 {start|stop|restart|status}"
    exit 1
    ;;
esac

exit 0
</code></pre>

<p>Above copied from <a href="https://github.com/fhd/init-script-template">this repo</a>.</p>

<p>When starting a long-running process (daemon), it was up to us to write handle logging, PID file creation, signal handling and more. When writing a ruby daemon, we wrap our script using the <a href="https://github.com/thuehlinger/daemons">daemons gem</a>, but this still required extra effort and the creation of a "control" script. Additionally, System V doesn't provide a way to monitor and ensure that a daemon is kept alive, meaning if our script fails it would not be restarted.</p>

<h3>Monit</h3>

<p>After passing on System V, <a href="https://mmonit.com/monit/">Monit</a> seems like a much better choice. It has a relatively easy configuration file syntax, can start our processes on boot, and can even ensure that the process is kept alive - if the process fails, monit can restart it automatically.</p>

<p>Monit itself is a daemon that can be installed via a package manager (in our case on Ubuntu and Rasbian) and is launched on boot. The monit process can be configured fairly easily to monitor and start scripts, although there are a few drawbacks that made monit our less than ideal choice.</p>

<h4>Pros</h4>

<ul>
  <li>Monit is launched on boot via System V</li>
  <li>Easy configuration sytnax</li>
  <li>Can restart a failed process automatically if desired</li>
  <li>Can kill and restart a process based on parameters such as CPU usage and memory consumption</li>
  <li>Does provide a formal way to declare that a monit service depends upon another monit service</li>
</ul>

<h4>Cons</h4>

<ul>
  <li>Requires installation, such as <a href="http://linux.die.net/man/8/apt-get">apt-get</a> on Raspian and Ubuntu</li>
  <li>Still requires us to handle daemonization ourselves, with logging, PID files, and signal handling</li>
  <li>Monit only provides a "sanitized" shell to launch processes, meaning a useless <code>$PATH</code> and more needs manipulating</li>
  <li>Monit is a "polling" process, meaning a process that dies can be dead until Monit checks again</li>
</ul>

<p>Ultimately we didn't go with Monit because we needed to ensure that one of our processes was running always - and there will always be a delay with Monit polling for statuses of each process at an interval.</p>

<h3>Runit</h3>

<p>We did not do a lot of research into Runit, simply because Upstart and Systemd were what was available on Ubuntu. It really only popped on our radar because of Mike Perham of Sidekiq fame.</p>

<h4>Cons</h4>

<ul>
  <li>Not available on Ubuntu or Raspian</li>
</ul>

<h3>Upstart</h3>

<p>Initially Upstart looked like the perfect solution, as it is the designated init system in Ubuntu 14.0 and has a clean syntax. However, the Linux community is attempting to converge on a standard, and with the release of Ubuntu 15.0 Systemd, and not Upstart, is the default init system. Combined with the fact that Raspian comes with Systemd instead of Upstart, and the choice was made for us.</p>

<h4>Cons</h4>

<ul>
  <li>Not available on Raspbian</li>
  <li>Deprecated on Ubuntu 15.0 release</li>
</ul>

<h3>Finally - Systemd</h3>

<p>The final solution we found involved using <a href="https://wiki.debian.org/systemd">Systemd</a> to manage our processes on boot and keep them alive reliably.</p>

<h4>Pros</h4>

<ul>
  <li>Installed and the default service manager on Ubuntu and Raspian</li>
  <li>Simple configuration syntax</li>
  <li>Can immediately restart a failed process</li>
  <li><em>No need to daemonize our script!</em></li>
  <li>Simple settings for user, working directory, and output</li>
  <li>Integrated with <a href="https://en.wikipedia.org/wiki/Syslog">syslog</a></li>
  <li>Allows for "pre" and "post" run scripts before executing the process</li>
</ul>

<h4>Cons</h4>

<ul>
  <li>Documentation didn't make it clear that your service configuration file could exist anywhere on the filesystem.</li>
</ul>

<h4>Example</h4>

<pre><code>[Unit]
Description=udp-listener
After=multi-user.target

[Service]
Type=idle
WorkingDirectory=/var/apps/our-software
ExecStart=/var/apps/our-software/bin/udp-listener
User=pi
Group=pi

# if we crash, restart
RestartSec=1
Restart=always

# Logs go to syslog
# View with "sudo journalctl -u udp-listener"
StandardOutput=syslog
StandardError=syslog

SyslogIdentifier=udp-listener

[Install]
WantedBy=multi-user.target
</code></pre>

<p>In the end, Systemd provided the exact functionality we required, without requiring another dependency such as Monit to manage our services and hope this helps you with your next IoT ruby development project!</p>]]></content><author><name>Matt</name></author><category term="ruby" /><category term="dev ops" /><category term="software" /><summary type="html"><![CDATA[For the past several months, we have been working on a software solution to run on embedded devices and recently found ourselves needing to solve the problem of how to run some of our software at determined times in the device lifecycle. In this post, we'll explore the solutions that we explored, why we didn't choose any of those solutions, and what we ended up with that has worked well.]]></summary></entry><entry><title type="html">Welcoming Lorena to Lotus</title><link href="https://www.madebylotus.com/blog/introducing-lorena/" rel="alternate" type="text/html" title="Welcoming Lorena to Lotus" /><published>2016-05-10T00:00:00+00:00</published><updated>2016-05-10T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/introducing-lorena</id><content type="html" xml:base="https://www.madebylotus.com/blog/introducing-lorena/"><![CDATA[<p>We're extremely happy to have hired Lorena back in February, and wanted to take a moment to share that news with you.</p>

<p>Lorena comes to us after completing a course at <a href="http://devbootcamp.com/">Dev Bootcamp</a>, where she studied to become a full stack Rails engineer.  Lorena has worked hard to familialize herself with service objects, cucumber BDD testing, and event React.js since joining Lotus on a variety of projects for our clients.</p>

<p>Lorena is a natural problem solver, has great communication skills and attention to detail, which has made her an integral part of our team in a matter of weeks.</p>

<blockquote>
  <p>Growing up I've always found satisfaction in problem solving, creating and building which brings me to the developer I am today. As a recent Dev Bootcamp graduate, I am excited to be a part of Lotus Apps!</p>
</blockquote>

<p>-- <em>Lorena</em></p>

<p>We're happy to have you onboard, Lorena!</p>]]></content><author><name>Matt</name></author><category term="operations" /><category term="hiring" /><summary type="html"><![CDATA[We're extremely happy to have hired Lorena back in February, and wanted to take a moment to share that news with you.]]></summary></entry><entry><title type="html">Adventures in Hiring an Engineer</title><link href="https://www.madebylotus.com/blog/job-posting-success/" rel="alternate" type="text/html" title="Adventures in Hiring an Engineer" /><published>2016-03-03T00:00:00+00:00</published><updated>2016-03-03T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/job-posting-success</id><content type="html" xml:base="https://www.madebylotus.com/blog/job-posting-success/"><![CDATA[<p>Growing to a point where Lotus could even think of bringing another engineer on the team was an exciting first step, and a scary realization as well.  However, we crunched our numbers, negotiated a couple new clients on a retainer contract to better secure long-term work, and dove in.</p>

<p>At first, we used our network of collegeues to field candidates, but that didn't take us very far for a couple reasons.  First, most of our collegues are great at what they do, meaning they're in much farther along in their careers and our needs just didn't line up.  From there we decided to post a job on <a href="http://weworkremotely.com">www.weworkremotely.com</a>, which we paid $250 for 30 days ($50 for being featured) although we only left the posting live for 2 weeks.</p>

<h2>Candidate Breakdown</h2>
<p>This isn't highly scientific, as we sift through a Gmail folder, but here is a general breakdown of applicants:</p>

<ul>
  <li>Roughly 200 total applicants, resulting in close to 450 emails (people asking for followup, us sending short responses).</li>
  <li>We had about 40 firms contact us from the posting, including phone calls. Most were from outside the US.</li>
  <li>Over half of the applicants did not meet basic criteria of a US timezone with some Rails experience.</li>
  <li>Of those applicants, we responded to about 25 either to schedule a phone screen, or to offer advice to those who put in effort in applying, yet did not meet our needs.</li>
  <li>Astounding the number of people who did not follow the directions on how to apply on the posting, and/or did not provide a resume or any basic contact info.</li>
  <li>Most of our phone screens were for recent dev bootcamp graduates.  The Iron Yard and Turing School were the most popular, in that order.</li>
</ul>

<h2>Interview Process</h2>

<p>Overall, the interview process itself was positive.  The candidates we did speak with were passionate, talented, and eager to learn making it a refreshing and fun experience.  We had one individual respond to our decline to chat with a threatening and unbalanced email, which was unsettling and strange to say the least - that was the only negative experience.</p>

<ul>
  <li>We scheduled 17 phone screens as our first step in speaking with a candidate.</li>
  <li>2 of the phone screens didn't show, and only 1 of those responded apologizing and asked to reschedule.</li>
  <li>We brought 4 candidates back for an hour long technical interview.</li>
</ul>

<p>Our phone screens consisted of a 20-30 minute video chat on Google Hangouts, and most candidates used that entire time.  It was unstructured, used as an introduction to our company, what we were looking for in a candidate, and a time for the candidate to share some about themselves and what they were hoping to find in their next career move.  We kept a Google Doc with notes such as hourly rate, location, availability, current employment or activity, and impressions which we reviewed after all phone screens were complete.</p>

<p>The technical interview was an hour long video chat on Google Hangouts, where we reviewed some intermediate and advanced discussion topics on Ruby, Ruby on Rails, and Git.  The candidate's responses largely drove the direction of the discussion, and was exploratory such as "Now that we know how Rails determines which migrations to perform on a database, how do you think you would build that feature?".  This approach highlighted critical thinking, ability to pivot to new information, how stressed a candidate was under light pressure and just in general how fun it would be to work with this person on a daily basis to solve problems together.</p>

<h2>Hindsight</h2>

<p>In hindsight, there are a few things we would have done differently to improve the process on ourselves and candidates.  First, setting up a dedicated Gmail inbox would have been an easy step to keep my inbox clearer, and to allow others in Lotus to more easily help screen incoming applications.  Second, being even more blatantly obvious on timezone restrictions and firms would have potentially weeded out some useless chatter.  Lastly, I severley underestimated the time required to do this justice - we tracked almost 40 hours for interviews, sifting through email and responding to the few candidates we did.</p>

<p>This was a great experience, where we got to meet several awesome people, have a little fun and ended up hiring a great fit for our team - stay tuned for a new post welcoming her officially!</p>]]></content><author><name>Matt</name></author><category term="operations" /><category term="hiring" /><summary type="html"><![CDATA[Growing to a point where Lotus could even think of bringing another engineer on the team was an exciting first step, and a scary realization as well. However, we crunched our numbers, negotiated a couple new clients on a retainer contract to better secure long-term work, and dove in.]]></summary></entry><entry><title type="html">Unify Sports</title><link href="https://www.madebylotus.com/blog/unify-sports/" rel="alternate" type="text/html" title="Unify Sports" /><published>2015-11-17T00:00:00+00:00</published><updated>2015-11-17T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/unify-sports</id><content type="html" xml:base="https://www.madebylotus.com/blog/unify-sports/"><![CDATA[<p>We're proud to be a part of all of our client's projects, but none more so than our friends over at Unify.  Unify has worked hard to bring simplicity and elegance to custom mobile apps for churches, and is now bringing that same touch to sports teams.</p>

<p>Unify Sports allows teams to create a customized and sleek mobile app for iOS and Android, to keep the entire team in touch via messaging, a team roster, practice and game schedules, photo albums, videos and more.  We've worked hard with Unify to bring our shared passion to life, crafting a platform experience above any competitor with a simple elegance that is a joy to use.</p>

<p>If you like Unify Sports, be sure to sign up. We'd love to hear from you if you're in the market for a firm to bring your creation to life, just <a href="/contact/">contact us</a>.</p>

<div class="row">
  <div class="col-sm-6">
    <a href="http://www.unifychurch.com" target="_blank" class="btn btn-dark btn-block">Unify Church</a>
  </div>
</div>]]></content><author><name>Matt</name></author><category term="client showcase" /><summary type="html"><![CDATA[We're proud to be a part of all of our client's projects, but none more so than our friends over at Unify. Unify has worked hard to bring simplicity and elegance to custom mobile apps for churches, and is now bringing that same touch to sports teams.]]></summary></entry><entry><title type="html">Introducing Prospector</title><link href="https://www.madebylotus.com/blog/introducing-prospector/" rel="alternate" type="text/html" title="Introducing Prospector" /><published>2015-09-12T00:00:00+00:00</published><updated>2015-09-12T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/introducing-prospector</id><content type="html" xml:base="https://www.madebylotus.com/blog/introducing-prospector/"><![CDATA[<blockquote>
  <p>You can skip this and check out Prospector yourself on the website if you're impatient.</p>
</blockquote>

<h2>Introducing Prospector</h2>

<p>We're so excited to unwrap and share the result of many months of hard work with the world, in our first app from Lotus.  Simply put, Prospector monitors your Ruby codebase to provide important insights into the health of your application by keeping track of both major and minor updates available for your project.</p>

<h3>How It Works</h3>

<p>Prospector can be installed in any Ruby based project that utilizes the magnificent <a href="http://bundler.io/">bundler</a>.  We gather usage details from your project's Gemfile and then continuosly check for updates from <a href="http://rubygems.org">rubygems.org</a> to find available updates.  Prospector summarizes major and minor updates available to your codebase into an overall health score, keeps track of your teams progress over time, and provides all the details you need to stay on top of updates in your Ruby codebase.</p>

<h3>Integrations</h3>

<p>Since the only requirement is a Ruby project, Prospector can be integrated into almost any codebase.  Installing in a Rails project is simplest, as we automatically detect if ActiveJob is configured, or Sidekiq to queue jobs to report to our API in the background.  In a non Rails project, you can call the method to report on your project directly however you see fit, and we plan to add more background queue adapters, as well as Rake tasks to make integrating with any Ruby project even easier.</p>

<h3>Get Started</h3>

<p>Prospector is an effort of love, as we saw the need for a conclusive overview of our codebases and decided to build it ourselves.  Prospector will only get better over time, and we can't wait to have more Ruby developers join the Prospector family.</p>]]></content><author><name>Matt</name></author><category term="ruby on rails" /><category term="product" /><category term="announcements" /><summary type="html"><![CDATA[You can skip this and check out Prospector yourself on the website if you're impatient.]]></summary></entry><entry><title type="html">Working Remote - Part 2</title><link href="https://www.madebylotus.com/blog/working-remote-part-2/" rel="alternate" type="text/html" title="Working Remote - Part 2" /><published>2015-06-03T00:00:00+00:00</published><updated>2015-06-03T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/working-remote-part-2</id><content type="html" xml:base="https://www.madebylotus.com/blog/working-remote-part-2/"><![CDATA[<p>This post is a follow up to my previous post on <a href="/blog/working-remote/">working remote</a> after receiving some feedback and questions.</p>

<h3>Travel</h3>
<p>I wrote fairly extensively about my surprise at the number of teams that require regular travel and most of the readers had the same response of amazement - I'll take an opportunity here to clarify a bit more however.</p>

<p>Travel has always been my personal pet peeve working remote. I find it very contradictory to be a remote employee or contractor and then be required to be on site every so often. I have worked a few places with no travel. One of those was based out of Vancouver, and is the team I mentioned frequently about being the most streamlined. The other times have been when I have contracted remotely on a short term or part time basis. Any time I have been a W2 employee and remote, travel has been what I consider too frequent. I use this as a filter question for recruiters and even when applying via email at this point to save both parties time.</p>

<p>What you consider too frequent might be significantly different of course, but I was able to say that if there was even a hint of being on site more frequently than once a quarter I would have to skip.  Essentially any time I have been a W2 employee working remote, I have been on site roughly every 8 weeks no matter the organization, with the one exception of Vancouver.</p>

<h3>Researching Companies</h3>
<p>I'm by no means an expert on vetting organizations, but have found that I drastically undervalued the opportunity to ask pertitent and tough questions early on in the interview process.  Most of my experiences were still wonderful, and I worked with amazing distributed teams.  However, there are a couple instances where I could have done a better job, especially where I found myself as an employee who didn't receive a paycheck for 4-5 months.</p>

<p>There can be a wide variety of things to check into. The simplest is to just ask one of the founders or someone VP level in the organization for their back story. I like to know how the founders came up with the idea, if they're still around, and what they bring to the table.</p>

<p>From a stability and financial persepctive, you can check glassdoor (not very useful for me most times with small companies), see if you can get connected with anyone who worked there previously via LinkedIn to pick their brain, or check out their business registration in whatever home state. Additionally, checking the Better Business Bureau for complaints, and just googling their company and founders to find any press sometimes turns up good information. Usually there's at least a few pieces on TechCrunch or some angel investor site that details more of their history, and it's a good time to verify that what you were told lines up.</p>

<p>I bring up investment questions in my 2nd chat, to see how many rounds of funding they've had and get the details. Most places will share when they received funding and for how much. Again, this info is usually available online because of press releases in the past. I always ask if they're seeking funding currently, when they expect to get that round, what they plan to do with it, when they expect to be profitable, what their business model is and how whatever it is they are offering, is different than all their competitors.</p>

<p>With all of that I think you really shine as a candidate who is interested in more than just the code or day to day operations, and you can judge for yourself if you're aligned with the company goals and ambitions.</p>

<h3>Finding Remote Work</h3>
<p>I was asked what I've used in the past to find remote work, and I can honestly say it's all boiled down to 3 main venues.</p>

<p>First, you cannot underestimate the power of online networking.  Not really LinkedIn, although that helps, but just in general connecting with other peers from open source projects, engaging in discussions on Github on projects you're using, etc.  I contributed to ActiveAdmin early on and had great engagement with Greg Bell <a href="https://twitter.com/gregbell">@gregbell</a> (Reverb, Medeo and now Hootsuite) and Philippe Creux (Reverb and now cofounder of <a href="http://www.brewhouse.io">Brewhouse.io</a>) which ended up landing me on the team with them at Reverb.</p>

<p>Outside of old fashioned networking, I find <a href="http://www.weworkremotely.com">weworkremotely</a> to be the best resource.  Because it is exclusively remote work (and not just engineering!) there is a relatively high level of competition of candidates, but I've had great experiences using the site.</p>

<p>Stack Overflow Careers also lets you create a profile and search for remote jobs, and I've found a few organizations on there that were not posted elsewhere.</p>

<h3>Comments?</h3>
<p>If you still have more questions, I'll be happy to answer any on twitter or if there is enough I might write a 3rd post on the subject.</p>]]></content><author><name>Matt</name></author><category term="remote work" /><category term="culture" /><summary type="html"><![CDATA[This post is a follow up to my previous post on working remote after receiving some feedback and questions.]]></summary></entry><entry><title type="html">Working Remote</title><link href="https://www.madebylotus.com/blog/working-remote/" rel="alternate" type="text/html" title="Working Remote" /><published>2015-04-16T00:00:00+00:00</published><updated>2015-04-16T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/working-remote</id><content type="html" xml:base="https://www.madebylotus.com/blog/working-remote/"><![CDATA[<p>It has been a whirlwind adventure, but I'm rapidly approaching 3 years as a remote engineer and realized I haven't once taken the time to share my thoughts on what has worked, what hasn't, and everything in between.</p>

<h3>My Background</h3>
<p>I started my career at a digital design and web development agency here in downtown Knoxville while finishing up my bachelor's in Computer Science.  I worked hard, learned a ton by being part of a small business, and wouldn't trade that experience for anything.  In fact, that time really prepared me for starting Lotus Apps and what it took to be a business owner.  I left that agency looking for a larger engineering team, the chance to work with seasoned Rails engineers, and just work with others outside of my comfort group to continue to learn and grow.  There was not, and still isn't, any opportunities here in town for Rails development, so I ended up taking the plunge into remote work.</p>

<p>I've had the opportunity to work solo on remote projects, as well as work on large (15 member) distributed engineering teams - one team had members in the US, Canada, France, Jamaica, and Columbia at the same time!  The constant is always the people - whether you're looking to join a team in town, or remote, find a team that you click with and want to work with every day.</p>

<h3>What Worked</h3>
<p>I'll say the biggest thing that helped me with remote work, was simply easing into it. I didn't drop everything and start full time, but rather began part time and built up to it.  Beyond that, here are my thoughts after 3 years of being on various teams.</p>

<h4>Communication is King</h4>
<p>Use whatever tools to stay in contact, from chat rooms to video chats and even the old fashioned phone.  I find it reassuring that if everything else fails, I have a phone number to call.</p>

<h4>Be Transparent</h4>
<p>Coworkers can not pick up on any "vibes" as easy when there is no coffee pot to gather around.  Voice concerns early, and listen to others to build trust.</p>

<h4>Get to Know Your Team</h4>
<p>Work can be fun, and hopefully it is. But I found I enjoyed and did my best work when the team got to know each other outside of "the office". A Google Hangout or phone call every so often to check up kids, spouses, hobbies - all that paid off to really build morale and make a place that I loved being a part of.  I still keep in touch with several of these folks, and I've never met them in person.</p>

<h4>Invest in Tools and Documentation</h4>
<p>Tools are going to be the way your team collaborates.  Ensure that your team has everything they need to remove barriers and communicate on a whim.  For engineers especially, this can be crucial to solving a problem quickly and effectively. Without good tools, you're stifling your team's creativity and productivity.</p>

<h4>Daily Standups</h4>
<p>I say this without meaning a strictly Scrum approach, but be sure to see your team every day and have an idea of what everyone is working on.</p>

<h3>Pitfalls</h3>
<p>Having worked with so many distributed teams, I share these as items that didn't work as well for me - your mileage may vary.</p>

<h4>Odd Man Out</h4>
<p>At this point in my career, I vary rarely consider an engagement where the organization has never had remote workers and I would be the only one.  It's a tough learning curve for the organization to keep you included, and you will find yourself needing to lead the conversation and direct to provide the best experience for you both.</p>

<p>That said, I would have missed working with the team I loved the most if I had stuck hard and fast to this rule, as I was the first remote engineer and the others followed.</p>

<h4>Trust</h4>
<p>It's harder to trust people that you don't know well.  From a pure business perspective, make sure you get plenty of details about an organization before joining. It's even more important when you're halfway across the country, to know things like mailing addresses, contact information, business standing, how well the company is funded, and more.  Do your homework.</p>

<h4>Disconnect</h4>
<p>In a remote team, or even as a solo remote engineer, you'll have to work even harder to build connections with others in your organization. Make yourself available and reach out to others.</p>

<p>I work in a home office most of my week, but find myself taking a trip to do grocery shopping, working from a coffee shop a morning a week, or working from a park crucial to keeping my sanity.  People crave being around other human beings, so find something that works for you.  Coworking spaces are popular in larger metropolitan areas for this reason I believe.</p>

<h4>Remote Team, Onsite Company</h4>
<p>I found myself on a team where the engineering team was remote, but the rest of the company shared an office space.  Overall it was a challenge to ensure that the organization felt like one cohesive unit.  Communication was a struggle because information naturally flows more freely in person, and the remote team found ourselves "the last to know" on many occasions.</p>

<h4>Travel</h4>
<p>For whatever reason, many places that are looking for remote engineers will want you to travel to do team building exercises.  I've been amazed interviewing with organizations that wanted me onsite for as much as 2 weeks every month, to about a week every 9 weeks.  About 90% of my trips to be onsite have been unproductive, disruptive, and expensive attempts that failed to deliver any value.  Inquire about travel policies early on if this is something you would want to avoid.</p>

<p>I believe regular communication, transparency and personal connections remove any need to be on-site for most remote engineering jobs.</p>

<h3>Legal Advice</h3>
<p>I write this section as a standalone, because it is a big one.  If you're considering remote work, there are two camps you can fall into as a US resident and have major impacts.</p>

<h4>1099 Contractor</h4>
<p>A 1099 contractor receives no benefits from the organization hiring you, and you are treated essentially as a business.  You want to impress your client, but you have the freedom to set your own hours, use the equipment you want to use, and more.</p>

<p>In this setup, you are responsible for billing your client and pursuing them if they do not pay.  Generally speaking, that involves writing a demand letter and possibly taking the client to court or sending the liability to a collections agency to collect on the debt for you.</p>

<p>If you're looking to purchase a home in the next couple years, be aware that there is a huge burden to prove you can afford a mortgage when your income is not via payroll, which is presumably more stable as viewed by the banks.</p>

<h4>W2 Employee</h4>
<p>The alternative is a W2 employee, where your organization actually registers in your state to hire you and pays you as any other employee.  They'll withhold the normal holdings on your pay checks and theoretically you are no different than if you had an office to physically go into every day.</p>

<p>In this setup, you are paid for the hours worked, or by a salary.  This makes banks happy to give you money for a mortgage, and can help you in the case that you are not given a paycheck that you deserve.</p>

<p>I was a remote employee in TN for an organization that never gave me my first pay check and ended up owing me $9k in gross wages before I ended my time there.  I had thought the state of TN would help me pursue the wages, as I was an employee with an offer letter agreement and the company had the legal obligation to deliver a final paycheck, pay for overtime, and withhold federal taxes.  However, in my case, the state of TN refused to investigate and pursue because the business was located in Washington DC, and both Deleware and DC refused to help because I had performed all the work in TN.  In the end, I had to sue the organization in civil court just as if I had been a 1099 contractor, and the protections that states are supposed to provide were of no help.</p>

<h3>Final Thoughts</h3>
<p>I hope that this sheds some light on what it is like to work as part of a remote team every day, and gives you some good questions to ask yourself and your potential employer if you're thinking about making the switch.  If you have anything specific to ask, just ask me on twitter!</p>]]></content><author><name>Matt</name></author><category term="remote work" /><category term="culture" /><summary type="html"><![CDATA[It has been a whirlwind adventure, but I'm rapidly approaching 3 years as a remote engineer and realized I haven't once taken the time to share my thoughts on what has worked, what hasn't, and everything in between.]]></summary></entry><entry><title type="html">Experience with Poltergeist and PhantomJS Testing</title><link href="https://www.madebylotus.com/blog/experience-with-poltergeist-and-phantomjs-testing/" rel="alternate" type="text/html" title="Experience with Poltergeist and PhantomJS Testing" /><published>2014-09-30T00:00:00+00:00</published><updated>2014-09-30T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/experience-with-poltergeist-and-phantomjs-testing</id><content type="html" xml:base="https://www.madebylotus.com/blog/experience-with-poltergeist-and-phantomjs-testing/"><![CDATA[<p>Let's go down the long path of integration testing, where teams seem scared to venture.</p>

<p>I've worked on a few teams where we used Cucumber or plain capybara for our integration testing layer, and that has always been backed with <a href="https://github.com/thoughtbot/capybara-webkit">capybara-webkit</a> as the JS driver for the headless browser.  In this article we'll explore the differences between <a href="https://github.com/thoughtbot/capybara-webkit">capybara-webkit</a> and <a href="http://phantomjs.org">phantomjs</a> as a driver for capybara.</p>

<h2>Examining capybara-webkit</h2>
<p>Until more recently, I believe <a href="http://phantomjs.org">phantomjs</a> just wasn't as reliable as <a href="https://github.com/thoughtbot/capybara-webkit">capybara-webkit</a> and that was the biggest motivating factor for not using it (correct me if I'm wrong).</p>

<h3>Installation</h3>
<p>Installing <a href="https://github.com/thoughtbot/capybara-webkit">capybara-webkit</a> is not as easy as it should be.  Installation on your computer requires a build of QT to link against, and QT is a massive download and install.</p>

<p>Additionally, installation and configuration on your CI server requires a headless X server, such as Xvfb.  There are more tools to install and more configuration with <a href="https://github.com/thoughtbot/capybara-webkit">capybara-webkit</a>.
Gems such as <a href="https://github.com/leonid-shevtsov/headless">headless</a> help a bit with the configuration in your Rails app, but the general requirements are still there.</p>

<h3>Features</h3>
<p>Capybara-webkit provides a JS enabled headless browser, allows you to render the page to HTML or an image and supports the vast library of capybara features.</p>

<h2>Examining phantomjs and poltergeist</h2>
<p>The <a href="https://github.com/teampoltergeist/poltergeist">poltergeist</a> gem provides a <a href="http://phantomjs.org">phantomjs</a> driver for capybara, same goal as capybara-webkit.  Phantomjs is also built on top of the webkit rendering engine, but that's about where the similarities between the two projects end.</p>

<h3>Installation</h3>
<p>Installation of phantomjs is much quicker on a Mac, as a QT installation is not required to build the project.  On our CI server we're running CentOS, which required sudo access to add a yum repo for installation of phantomjs.</p>

<pre><code>[stevenmerrill-phantomjs]
name=Copr repo for phantomjs owned by stevenmerrill
baseurl=http://copr-be.cloud.fedoraproject.org/results/stevenmerrill/phantomjs/epel-6-$basearch/
skip_if_unavailable=True
gpgcheck=0
enabled=1
</code></pre>

<p>After the yum repo was installed, installing phantomjs on our CI server was fairly simple.</p>

<pre><code>sudo yum install phantomjs
</code></pre>

<h3>Features</h3>
<p>Phantomjs offers all the same features as capybara-webkit, but with a much easier installation.  Additionally however, I really like the option to raise an error in Ruby when there is an error in the headless browser.</p>

<h4>Exception Handling</h4>
<p>A great example of how this is beneficial could be testing a page that happens to load a JS library twice, or even a simple parse error in JS.  <a href="http://phantomjs.org">Phantomjs</a> will raise a Ruby exception, with a backtrace into your issue, allowing you to much more easily track down the problem.</p>

<p>With capybara-webkit, your expectation in capybara would fail due to an element not being present on the page, but you would have no clue why.  In the past I've debugged scenarios like this by saving the HTML of the page, opening a browser on the port of the currently running Capybara server, or switching to Selenium and a pause statement to examine the contents while the test runs.</p>

<h4>Network Traffic</h4>
<p>Another awesome feature to make use of was inspecting network traffic.  In my capybara step definition, I could inspect all the requests the <a href="http://phantomjs.org">phantomjs</a> browser had made, including request headers, response headers and the respond body, with the following command:</p>

<pre><code>page.driver.network_traffic
</code></pre>

<p>With this command I noticed that our test suite was loading the Bootstrap CSS library from a CDN in every test, and was able to properly configure <a href="https://github.com/vcr/vcr">VCR</a> in our test project to cache the contents to speed up our tests.</p>

<p>You could also make great use of this to inspect Ajax calls made during your JS browser session and ensure that the server response was what you expected.</p>

<p>Phantomjs just provided a stellar debugging experience overall compared to capybara-webkit.</p>

<h2>Conclusion</h2>
<p>The actual conversion from one web driver to another in our project was actually pretty painless.  We did not have any tests fail, the configuration was straight forward, and configuring the CI server was easy.</p>

<p>I hope that helps convince you and your team to take poltergeist for a spin in your test suite and see how much time you can save debugging those painful scenarios that fail.</p>

<p>Displays images in a grid, with individual pages optionally describing the image in detail, or displaying more images. Images have a max-width of 400 pixels by default.</p>]]></content><author><name>Matt</name></author><category term="testing" /><category term="ruby" /><category term="rspec" /><summary type="html"><![CDATA[Let's go down the long path of integration testing, where teams seem scared to venture.]]></summary></entry><entry><title type="html">Introducing ProMotion-Menu</title><link href="https://www.madebylotus.com/blog/introducing-promotion-menu/" rel="alternate" type="text/html" title="Introducing ProMotion-Menu" /><published>2014-08-28T00:00:00+00:00</published><updated>2014-08-28T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/introducing-promotion-menu</id><content type="html" xml:base="https://www.madebylotus.com/blog/introducing-promotion-menu/"><![CDATA[<p>I'd like to formally announce that one of my gems for RubyMotion has seen a major feature upgrade, a name change, and has a new owner on Github!</p>

<p><a href="https://github.com/clearsightstudio/ProMotion-menu">ProMotion-menu</a>, formally <code>pro_motion_slide_menu</code>, has been transferred to the amazing guys at Clearsightstudio.  It was a natural fit, since Jamon and the team are the guys behind the popular ProMotion gem itself, and consolidating all the supporting feature gems under one organization just makes sense.</p>

<h2>Official 1.0 Release</h2>
<p>ProMotion-menu will be seeing an official 1.0 release soon, with a first beta available now to those interested.  The list of changes is significant:</p>

<ul>
  <li>Ditched <a href="https://github.com/pkluz/PKRevealController">PKRevealController</a> for <a href="https://github.com/mutualmobile/MMDrawerController">MMDrawerController</a></li>
  <li>Much improved gesture support</li>
  <li>Simpler usage and method names</li>
  <li>Better test coverage</li>
  <li>From here out, better maintained with more frequent releases</li>
</ul>

<h2>Previous Gem Users</h2>
<p>What does this mean for users of <code>pro_motion_slide_menu</code>? The big change is that there will be no more releases for this gem, ever.  Any bug fixes and feature requests will be developed under the new gem.</p>

<p>You are encouraged to upgrade to ProMotion-menu when we have a final 1.0 release soon.  We will be posting a migration guide on the repo to help make for an easy transition.</p>]]></content><author><name>Matt</name></author><category term="ruby-motion" /><category term="open-source" /><category term="ios" /><summary type="html"><![CDATA[I'd like to formally announce that one of my gems for RubyMotion has seen a major feature upgrade, a name change, and has a new owner on Github!]]></summary></entry><entry><title type="html">Rails 4 Request Variants</title><link href="https://www.madebylotus.com/blog/rails-4-request-variants/" rel="alternate" type="text/html" title="Rails 4 Request Variants" /><published>2014-07-14T00:00:00+00:00</published><updated>2014-07-14T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/rails-4-request-variants</id><content type="html" xml:base="https://www.madebylotus.com/blog/rails-4-request-variants/"><![CDATA[<p>We explore the use of new request variants introduced in Rails 4 to render different layouts and views for mobile users.</p>

<h2>Setting a Request Variant</h2>

<p>Request variants in themselves are nothing more than a way to further distinguish your views and responses during rendering.  A common example, and one that we'll cover here, is rendering different views for mobile versus desktop.</p>

<p>A request may have multiple variants, and you set the variant for a request simply enough.  Below I make use of the excellent <a href="https://github.com/fnando/browser">browser</a> gem.</p>

<pre><code>class ApplicationController &lt; ActionController::Base
  before_action :set_request_variant

  private def set_request_variant
    request.variant = :mobile if browser.mobile?
  end
end
</code></pre>

<p>If you had reason, say to see if you were responding to a variant, you might do the following.</p>

<pre><code>class ApplicationController &lt; ActionController::Base
  helper_method :mobile_request?

  private def mobile_request?
    # variant is nil until you set it, but then is Array
    Array(request.variant).include? :mobile
  end
end
</code></pre>

<h2>Responding</h2>

<p>Variants are useful because they allow you even finer grain control over your responses.  Our simplest example is a controller action responding to both mobile and desktop HTML.</p>

<pre><code>class ProductsController &lt; ApplicationController
  def index
  end
end
</code></pre>

<p>Rails responds with HTML by default, so assuming you have two view files for each variant, the above will just work (more on what the file names are to come).</p>

<h2>Different Responses</h2>

<p>Part of the power of variants lie in the ability to further refine behavior, even for the same mime type response.</p>

<pre><code>class ProductsController &lt; ApplicationController
  def index
    respond_to do |format|
      format.html.mobile do
        # do something specific for mobile variant
      end
    end
  end
end
</code></pre>

<p>Here's an example showing a different nesting style.</p>

<pre><code>class ProductsController &lt; ApplicationController
  def index
    respond_to do |format|
      format.html do |html|
        html.mobile { redirect_to root_path }
      end
    end
  end
end
</code></pre>

<p>Finally, you might find use for the following if you want to customize the response without a variant and with.</p>

<pre><code>class ProductsController &lt; ApplicationController
  def index
    respond_to do |format|
      format.html do |html|
        html.any { render layout: 'simple' }
        html.mobile { redirect_to root_path }
      end
    end
  end
end
</code></pre>

<h2>Responding</h2>

<p>Rails uses a file naming convention to map variants to view files, and if the file exists it will be used to render the response.</p>

<p>The view files for our above action would be named the following, using the convention of a "+" with the variant in the filename after the mime type extension.</p>

<pre><code>app/views/products/index.html.haml
app/views/products/index.html+mobile.haml
</code></pre>

<p>You could certainly expand this and find it useful in rendering your JS responses as well.</p>

<pre><code>app/views/products/index.js+mobile.erb
</code></pre>

<h2>Layouts</h2>

<p>You can easily use variants in more than just your views or partials, layouts are fair game.</p>

<pre><code>app/views/layouts/application.html.haml
app/views/layouts/application.html+mobile.haml
</code></pre>

<p>If the request variant is set to <code>mobile</code>, then the mobile layout will automatically be used.  Otherwise the default layout is used as a fallback.</p>

<p>Note that the file and for a layout is evidently different than for a view here, the <code>+mobile</code> comes after the <code>.html</code>.</p>

<p>In the project I worked on, I had a few controllers that I absolutely did not want to render the mobile layout, even if it was requested on a mobile device.  Since we just used a before filter in our controller to set the variant, explicitly skipping that before filter per controller removes the variant and skips the mobile layout.</p>

<pre><code>class ProductsController &lt; ApplicationController
  skip_before_action :set_request_variant
end
</code></pre>

<p>Because I'm a big fan of code reuse across projects and organization, I refactored all of this out into a controller concern.</p>

<pre><code>module MobileVariants
  extend ActiveSupport::Concern

  included do
    before_action :determine_mobile_variant
    helper_method :mobile_request?
  end

  module ClassMethods
    def skip_mobile_variant(*args)
      skip_before_action :determine_mobile_variant, *args
    end
  end

  private def determine_mobile_variant
    request.variant = :mobile if browser.mobile?
  end

  private def mobile_request?
    Array(request.variant).include? :mobile
  end
end
</code></pre>

<p>Using this concern is simplistic and shows clear intention.</p>

<pre><code>class ApplicationController &lt; ActionController::Base
  include MobileVariants
end

class ProductsController &lt; ApplicationController
  skip_mobile_variant only: :edit
end
</code></pre>

<h2>Wrap Up</h2>

<p>Variants are just one of the major new features introduced in Rails 4.1, but I think will be a major staple of any Rails developer toolkit moving forward.</p>]]></content><author><name>Matt</name></author><category term="ruby-on-rails" /><category term="development" /><summary type="html"><![CDATA[We explore the use of new request variants introduced in Rails 4 to render different layouts and views for mobile users.]]></summary></entry><entry><title type="html">Introducing Swift</title><link href="https://www.madebylotus.com/blog/introducing-swift/" rel="alternate" type="text/html" title="Introducing Swift" /><published>2014-06-03T00:00:00+00:00</published><updated>2014-06-03T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/introducing-swift</id><content type="html" xml:base="https://www.madebylotus.com/blog/introducing-swift/"><![CDATA[<p>At WWDC 2014 Apple introduced a new official programming language called Swift to replace the aged Objective-C.  In this article we'll explore some of the similiarities and differences between Swift and RubyMotion.</p>

<h2>RubyMotion vs Swift</h2>

<p>First off, RubyMotion itself is a toolkit and chain to build iOS applications written in Ruby that is bridged with Objective-C.  Entire applications or just static libraries can be created. Swift is just a new language supported natively by Apple and it's build tools.</p>

<h3>Swift Impressions</h3>
<p>I've looked through much of Apple's documentation on the newly announced language and have found several concepts that I welcome as changes to writing iOS or Mac applications over Objective-C, as well as some things that Ruby provides that I hope make it over to Swift in the future.</p>

<h4>Welcome Changes</h4>

<ul>
  <li>Ditches the bracket and semicolon syntax</li>
  <li>Ditches pointers</li>
  <li>Typing is improved and easier to use</li>
  <li>Simpler object initialization and method calling syntax</li>
  <li>Bridging for new <code>String</code> classes or even <code>Selector</code> instances is simple and intuitive</li>
  <li>Extensions allow easily adding behavior to existing classes without subclassing</li>
  <li>Closure/block syntax is improved</li>
  <li>No need for <code>NSString</code> vs <code>NSMutableString</code> anymore. Declare your string as a variable using <code>var</code> or a constant using <code>let</code> and that determines mutability</li>
  <li><code>String</code> equality is finally achievable just via <code>==</code> operator</li>
  <li>Typed arrays declared as <code>String[]</code></li>
  <li>Switch statements break on the first match without the specific need for a <code>break</code> call</li>
  <li>Ranges!</li>
</ul>

<h4>Missing or Annoyances in Swift</h4>

<ul>
  <li>Parenthesis are not optional for calling methods - would much rather write <code>view.update</code> vs <code>view.update()</code> especially</li>
  <li><code>var</code> and <code>let</code> seem archaic and odd to me.  Would have much rather used Ruby's way here</li>
  <li>Would have loved to see a <code>method_missing</code> implementation from Ruby for extra dynamicism</li>
  <li>Do not like the <code>println("my \(variable)")</code> string interpolation syntax at all - just not a fan of using the backslash</li>
  <li>Not a fan of intializing dictionary literals with brackets, ie: <code>var dict:Dictionary&lt;String, String&gt; = ["key": "value"]</code> seems out of place to me.  Would have rather seen: <code>var dict:Dictionary&lt;String, String&gt; = { "key": "value" }</code></li>
  <li><code>func</code> doesn't sit just right with me.  I'm not sure what I would want in place, it just catches me off guard - not a big deal obviously.</li>
</ul>

<h3>Remaining Questions</h3>
<p>There are a few things I haven't yet figured out and would love to know if you have the answers to.</p>

<ul>
  <li>Is there any callback such as the <code>Module.included</code> method, when importing a namespace?</li>
  <li>Is something similar to <code>define_method</code> from Ruby available?</li>
  <li>Do you have to include a superclass when defining a class with protocols? Meaning is the following valid? <code>class MyClass: NSCopyingProtocol</code></li>
  <li>Have I missed a way to implement <code>method_missing</code>? Guessing no since Swift uses the same runtime as Objective-C</li>
</ul>

<h2>RubyMotion</h2>
<p>RubyMotion still has several perks, however one can definitely argue that the list was much longer before WWDC this year.</p>

<ul>
  <li>Any IDE of your choice</li>
  <li>Extensive collection of Ruby gems from the community</li>
  <li>More people know Ruby compared to Swift</li>
  <li>I think Ruby will still be a more natural choice for defining intuitive and gorgeous DSLs</li>
  <li>Support for iOS, Mac and Android (coming in version RM 3.0) with one language</li>
</ul>]]></content><author><name>Matt</name></author><category term="ruby-motion" /><category term="ios" /><summary type="html"><![CDATA[At WWDC 2014 Apple introduced a new official programming language called Swift to replace the aged Objective-C. In this article we'll explore some of the similiarities and differences between Swift and RubyMotion.]]></summary></entry><entry><title type="html">Introducing ActiveAdmin-StateMachine</title><link href="https://www.madebylotus.com/blog/active-admin-state-machine/" rel="alternate" type="text/html" title="Introducing ActiveAdmin-StateMachine" /><published>2014-04-08T00:00:00+00:00</published><updated>2014-04-08T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/active-admin-state-machine</id><content type="html" xml:base="https://www.madebylotus.com/blog/active-admin-state-machine/"><![CDATA[<p>In the previous post we examined meta-programming in RubyMotion and using the text-to-speech functionality available in iOS7.</p>

<p>In this article we are taking a look at a very large Rails engine, <a href="https://github.com/gregbell/active_admin">ActiveAdmin</a>, and a way to integrate <a href="https://github.com/pluginaweek/state_machine">state_machine</a>.  There is no reason you cannot use both without a gem tying these together, but of course more DSL makes things cleaner for repetitive tasks!</p>

<p>The gem providing a simple DSL that looks right at home in ActiveAdmin is <a href="https://github.com/macfanatic/active_admin-state_machine">ActiveAdmin-StateMachine</a>.</p>

<h2>Our reasoning</h2>
<p>Why would we want to create a gem adding even more DSL to ActiveAdmin?  Let's consider the scenario of managing some content, say a blog <code>Post</code>.  The <code>Post</code> can be in several states, going from draft, to published, and to archived.</p>

<p>This state machine is exactly why we would want to use the state_machine gem itself, which defines the logic and workflow that the <code>Post</code> goes through in our domain.  However, we end up writing a lot of controller code to implement the functionality to move the <code>Post</code> through the workflow.</p>

<ul>
  <li>We need to ensure the user is authorized to move the <code>Post</code> to the next step in the workflow.</li>
  <li>We write a lot of conditional UI to determine what actions are indeed available to move the <code>Post</code> to.</li>
  <li>The controller actions turn out to be pretty cookie-cutter after a few, and there is our hint to refactor!</li>
</ul>

<h2>Post state machine</h2>
<p>What follows is our example <code>Post</code> class which has an imaginable workflow for management.</p>

<pre><code># app/models/post.rb
class Post &lt; ActiveRecord::Base
  attr_accessible :body, :status, :title

  validates :title, presence: true, uniqueness: true
  validates :body, presence: true

  DRAFT = 'draft'
  REVIEWED = 'reviewed'
  PUBLISHED = 'published'

  state_machine :status, initial: DRAFT do
    event :peer_review do
      transition DRAFT =&gt; REVIEWED
    end

    event :publish do
      transition REVIEWED =&gt; PUBLISHED
    end
  end
end
</code></pre>

<h2>Controller actions</h2>
<p>In the simplest example of creating a controller action, we have exposed a new method <code>state_action</code> that appears to be just another ActiveAdmin DSL method at our disposal.</p>

<p>We see that we can pass blocks for further define the controller action as we see fit, but most of our configuration is handled with a few options and sane defaults of I18n translations.</p>

<p>The reason we can get by with this is because state_machine exposes the following methods on our <code>Post</code> class:</p>

<pre><code>p = Post.new
p.can_publish?
p.publish!
</code></pre>

<p>With those dynamic methods defined, our controller action that <code>state_action</code> creates for us starts to come into focus:</p>

<pre><code>class PostsController
  def publish
    if resource.can_publish?
      resource.publish!
      redirect_to smart_resource_url, alert: "post was published!"
    end
  end
end
</code></pre>

<p>The full finished implementation that creates the controller action follows below.  There are a few fun things to note:</p>

<ul>
  <li>We make use of ActiveAdmin's <code>member_action</code> to specify a code block for the controller action body, but also provide a HTTP verb which translates into the routing system.</li>
  <li>We allow the user to pass lambdas for several options so that they can determine a I18n string or message in a view context, but the defaults are very sensible and helpful.</li>
  <li>ActiveAdmin is built itself on top of <a href="https://github.com/josevalim/inherited_resources">InheritedResources</a>, which provides a lot of the subsystem that you might not be familiar with, such as <code>smart_resource_url</code>.</li>
  <li>We're additionally defining an <em>action item</em> for the state, such as "Publish".  In ActiveAdmin, an action item is a button that is shown when viewing the resource.</li>
</ul>

<p>This appears as:</p>

<pre><code>module ActiveAdmin
  module StateMachine
    module DSL

      #
      # Easily tie into a state_machine action
      #
      # @param [Symbol] state machine event, ie: :publish
      # @param [Hash] options
      #   - permission [Symbol] permission to check authorization against
      #   - http_verb [Symbol] :put, :post, :get, etc
      #
      # Will call "resource.publish!", if "resource.can_publish?" returns true
      #

      def state_action(action, options={}, &amp;controller_action)
        singular = config.resource_name.singular
        plural = config.resource_name.plural

        options[:permission] ||= controller.new.send(:action_to_permission, action)
        confirmation = options.fetch(:confirm, false)
        if confirmation == true
          default = "Are you sure you want to #{action.to_s.humanize.downcase}?"
          confirmation = -&gt;{ I18n.t(:confirm, scope: "#{plural}.#{action}", default: default) }
        end

        http_verb = options.fetch :http_verb, :put

        action_item only: :show do
          if resource.send("can_#{action}?") &amp;&amp; authorized?(options[:permission], resource)
            path = resource_path &lt;&lt; "/#{action}"
            label = I18n.t("#{plural}.#{action}.label", default: action.to_s.titleize)

            link_options = {}
            if confirmation.is_a?(Proc)
              link_options[:data] ||= {}
              link_options[:data][:confirm] = instance_exec(&amp;confirmation)
            end

            link_options[:class] = "btn btn-large"
            link_options[:method] = http_verb

            link_to label, path, link_options
          end
        end

        unless block_given?
          controller_action = -&gt; do
            resource.send("#{action}!")
            flash[:notice] = t("#{plural}.#{action}.flash.success")
            redirect_to smart_resource_url
          end
        end

        member_action action, method: http_verb, &amp;controller_action
      end

    end
  end
end
</code></pre>

<h2>Wrapping up</h2>
<p>I wanted to introduce this gem as a super convenient way to integrate state_machine into your ActiveAdmin project.  The UI for moving the resource through the state machine is completed generated from the state machine itself, and everything respects your CanCan ability (or custom ActiveAdmin authorization adapter).</p>

<p>Be sure to take a look at the <a href="https://github.com/macfanatic/active_admin-state_machine#custom-block">project README</a> more in depth for full example usage, including the fact that this gem is helpful even without state_machine!</p>

<h2>Up next</h2>
<p>What do you want to see in the next article, connect via twitter <a href="https://twitter.com/madebylotus">@madebylotus</a> and let me know!</p>]]></content><author><name>Matt</name></author><category term="ruby-motion" /><category term="ios" /><summary type="html"><![CDATA[In the previous post we examined meta-programming in RubyMotion and using the text-to-speech functionality available in iOS7.]]></summary></entry><entry><title type="html">Introducing Motion-Speech</title><link href="https://www.madebylotus.com/blog/motion-speech/" rel="alternate" type="text/html" title="Introducing Motion-Speech" /><published>2014-03-30T00:00:00+00:00</published><updated>2014-03-30T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/motion-speech</id><content type="html" xml:base="https://www.madebylotus.com/blog/motion-speech/"><![CDATA[<p>In our last post we looked into the mechanics of creating a gem for RubyMotion, detailing the structure, spec coverage and basics of getting your files into the RubyMotion "pipeline" to be included in the compilation process.</p>

<p>Since we have covered the basics previously, in this article we will spend most of our time in the code itself and have fun with dynamic methods, text-to-speech functionality, and method delegation.</p>

<p>We will examine the <a href="https://github.com/macfanatic/motion-speech#usage">motion-speech</a> gem created for this article.</p>

<h2>Getting Started</h2>
<p>Before we get started please take a minute to familiarize yourself with the two concepts we'll be covering in depth. The first being meta-programming in Ruby, and specifically what you can do with RubyMotion. The second the somewhat simple read of AVSpeechSynthesizer and friends.</p>

<h3>Ruby meta-programming</h3>
<p>Before we go any further, I encourage you to read <a href="http://clayallsopp.com/posts/rubymotion-metaprogramming/">this excellently detailed article from the wonderful Clay Allsopp</a> for lots of great meta-programming techniques.  Clay covers dynamic methods, singleton methods, class methods, instance_eval and more.  In my opinion, this is a must read if you want to begin to unlock the true power RubyMotion affords you in expressiveness and the ability to construct a DSL for your own use.</p>

<h3>AVSpeechSynthesizer</h3>
<p>For the purposes of this gem, we will be taking a look at how to make your iOS device running at least iOS7+ talk.  <code>AVSpeechSynthesizer</code> provides an interface to convert text to speech on your iOS device.</p>

<p>The synthesizer itself is created and can optionally inform a delegate of when it begins, pauses, or finishes speech.  The unit of speech itself is an <em>utterance</em> in Apple parlance.  Coincidentally, you ask the synthesizer to begin speech of your utterance, an instance of AVSpeechUtterance.</p>

<p>The utterance itself is where most of the customization can occur.  You can configure delays before or after the synthesizer speaks the utterance, change the rate at which it is spoken, set a different voice, pitch or any other number of factors to customize the experience.</p>

<h2>Motion-Speech</h2>
<p>To really make working with <code>AVSpeechSynthesizer</code> and <code>AVSpeechUtterance</code> much more ruby-like and fun, we will create a gem that provides the following features:</p>

<ul>
  <li>Pass a string and have it spoken without creating all the needed Cocoa classes, for convenience.</li>
  <li>Pass a block that will be called upon completion of the string</li>
  <li>Pass a block accepting one argument, to which you can further customize the ways you respond to an utterance being paused, stopped, or started.</li>
  <li>Provide simple methods such as <code>pause:</code>, or <code>resume</code> to control playback.</li>
  <li>Provide a simple way for any class, not just a string, to be passed to our speaker and be spoken as we want.</li>
</ul>

<p>This interface might look like the following, and in fact it does.  This is taken directly from the README of the <a href="https://github.com/macfanatic/motion-speech#usage">motion-speech</a> gem created for this article.</p>

<pre><code># Speak a sentence
Motion::Speech::Speaker.speak "Getting started with speech"

# Control the rate of speech
Motion::Speech::Speaker.speak "Getting started with speech", rate: 1

# Control the pitch
Motion::Speech::Speaker.speak "Getting started", pitch: 2.0

# Custom voice
voice_ref = AVSpeechSynthesisVoice.voiceWithLanguage("some_lang")
Motion::Speech::Speaker.speak "lorem", voice: voice_ref

# Pass a block to be called when the speech is completed
Motion::Speech::Speaker.speak "Getting started with speech" do
  puts "completed the utterance"
end
</code></pre>

<p>Providing a <em>speakable</em> interface looks like:</p>

<pre><code>class Name &lt; String
  def to_speakable
    "My name is #{self}"
  end
end

my_name = Name.new("Matt Brewer")
Motion::Speech::Speaker.speak my_name
# =&gt; "My name is Matt Brewer" spoken
</code></pre>

<p>Providing a way to further monitor events via a block syntax:</p>

<pre><code>Motion::Speech::Speaker.speak "lorem" do |events|
  events.start do |speaker|
    puts "started speaking: '#{speaker.message}'"
  end

  events.finish do |speaker|
    puts "finished speaking: '#{speaker.message}'"
  end

  events.pause do |speaker|
    puts "paused while speaking: '#{speaker.message}'"
  end

  events.cancel do |speaker|
    puts "canceled while speaking: '#{speaker.message}'"
  end

  events.resume do |speaker|
    puts "resumed speaking: '#{speaker.message}'"
  end
end
</code></pre>

<p>And lastly, controlling playback:</p>

<pre><code>speaker = Motion::Speech::Speaker.speak "lorem"

# pausing playback accepts symbols or constants
speaker.pause :word
speaker.pause :immediate
speaker.pause AVSpeechBoundaryImmediate

speaker.paused?
=&gt; true

speaker.speaking?
=&gt; false

# stopping playback accepts symbols or constants
speaker.stop :word
speaker.stop :immediate
speaker.stop AVSpeechBoundaryImmediate

# resume playback
speaker.resume
</code></pre>

<h3>Text-to-speech</h3>
<p>What follows is an excerpt of the <code>Motion::Speech::Speaker</code> class that has been modified to show what the minimum set of functionality to allow speech might look like.</p>

<pre><code>module Motion
  module Speech
    class Speaker
      attr_reader :message, :options

      MultipleCallsToSpeakError = Class.new(StandardError)

      def self.speak(*args, &amp;block)
        new(*args, &amp;block).speak
      end

      def initialize(speakable, options={}, &amp;block)
        @message = string_from_speakable(speakable)
        @options = options
        @spoken = false

        if block_given?
          if block.arity == 0
            events.finish &amp;block
          elsif block.arity == 1
            block.call events
          else
            raise ArgumentError, 'block must accept either 0 or 1 arguments'
          end
        end
      end

      def speak
        raise MultipleCallsToSpeakError if @spoken

        synthesizer.speakUtterance utterance
        @spoken = true
        self
      end

      def utterance
        return @utterance unless @utterance.nil?

        @utterance = AVSpeechUtterance.speechUtteranceWithString(message)
        @utterance.rate = options.fetch(:rate, 0.15)
        @utterance
      end

      def synthesizer
        @synthesizer ||= AVSpeechSynthesizer.new.tap { |s| s.delegate = self }
      end

      private

      def events
        @events ||= EventBlock.new
      end

      def string_from_speakable(speakable)
        if speakable.respond_to?(:to_speakable)
          speakable.to_speakable
        else
          speakable
        end
      end
    end
  end
end
</code></pre>

<p>There are a few things to take note of here:</p>

<h4>Creating an Error Class</h4>
<p>We can easily create a subclass using the <code>Class.new</code> syntax and assigning that to a constant. In this case we use the following to create an exception class.</p>

<pre><code>MultipleCallsToSpeakError = Class.new(StandardError)
</code></pre>

<h4>Splat Operator</h4>
<p>Your good friend the splat operator still works as you expect. There is no need to define what arguments the class method <code>speak</code> requires since really it is the same listing as what <code>initialize</code> expects.</p>

<pre><code>def self.speak(*args, &amp;block)
  new(*args, &amp;block).speak
end
</code></pre>

<h4>Block Arity and Inspection</h4>
<p>All of the methods provided for the inspection of a block still exist. We use this to our advantage - if you pass a block accepting no arguments, we assume you want the block called when the utterance is completed.  If you pass a block accepting 1 argument, we pass you an instance of <code>Motion::Speech::EventBlock</code> allowing further customization.  If your block accepts 2+ arguments, you're doing something wrong.</p>

<pre><code>if block_given?
  if block.arity == 0
    events.finish &amp;block
  elsif block.arity == 1
    block.call events
  else
    raise ArgumentError, 'block must accept either 0 or 1 arguments'
  end
end
</code></pre>

<h4>respond_to?</h4>
<p><code>respond_to?</code> works fine here, and we'll use it to see if our message provides a string.</p>

<pre><code>def string_from_speakable(speakable)
  if speakable.respond_to?(:to_speakable)
    speakable.to_speakable
  else
    speakable
  end
end
</code></pre>

<h3>EventBlock and meta-programming</h3>
<p>Now getting to the point of the article with some more meta-programming, we'll examine the <code>Motion::Speech::EventBlock</code> class.</p>

<pre><code>module Motion
  module Speech
    class EventBlock

      Events = %w(start finish cancel pause resume).freeze

      Events.each do |method|
        define_method method do |*args, &amp;block|
          if !block.nil?
            instance_variable_set("@#{method}_block", block)
          else
            instance_variable_get("@#{method}_block")
          end
        end
      end

      def call(event, speaker)
        block = send(event)
        block.call(speaker) unless block.nil?
      end
    end
  end
end
</code></pre>

<p>The following class illustrates usage of several useful concepts:</p>

<ul>
  <li><code>#define_method</code> works as you would expect, use it to your advantage.</li>
  <li><code>#instance_variable_get</code> and <code>#instance_variable_set</code> are amazingly helpful when in REPL and poking through your app, but they can be useful in crafting DSL classes as well.</li>
  <li>Don't be afraid to use <code>#send</code>, <code>#public_send</code> and friends.</li>
</ul>

<p>What does this class actually accomplish? It provides a method that when given a block, will store the block, and if the method is called without a block, it returns the block.</p>

<pre><code>event = Motion::Speech::EventBlock.new
event.start { true }
event.start.should.be.instance_of Proc

event.finish.should.be.nil

event.call(:finish, nil).should.be.nil
event.call(:start, nil).should.be.true
</code></pre>

<p>An instance of <code>EventBlock</code> is what allows the user of the <code>Speaker</code> class to easily listen in on callbacks from the synthesizer as the state changes.  <code>Speaker</code> itself is configured as the delegate of the synthesizer and implements the related delegate methods.</p>

<pre><code>module Motion
  module Speech
    class Speaker

      def speechSynthesizer(s, didFinishSpeechUtterance: utterance)
        events.call :finish, self
      end

      def speechSynthesizer(s, didStartSpeechUtterance: utterance)
        events.call :start, self
      end

      def speechSynthesizer(s, didCancelSpeechUtterance: utterance)
        events.call :cancel, self
      end

      def speechSynthesizer(s, didPauseSpeechUtterance: utterance)
        events.call :pause, self
      end

      def speechSynthesizer(s, didContinueSpeechUtterance: utterance)
        events.call :resume, self
      end

      def events
        @events ||= EventBlock.new
      end

    end
  end
end
</code></pre>

<p>If you noticed in the <code>Speaker#initialize</code> method, if the block given does not accept any arguments, we store that block as the callback for completion with a call to <code>events.finish &amp;block</code>.  Optionally if the given block accepts 1 argument we call the block and pass it the <code>events</code> instance to customize as the user sees fit.</p>

<h2>Wrapping Up</h2>
<p>What are some examples of ways to continue working on this gem?</p>

<ul>
  <li>Maybe <code>Motion::Speech::Speaker</code> could be a subclass of <code>AVSpeechSynthesizer</code> since so much of the workings of the class are based upon the synthesizer.</li>
  <li>Possibly subclass AVSpeechSynthesisVoice to make finding a voice and applying it to an utterance even easier.</li>
</ul>

<p>Would love to hear your ideas and comments on the article and gem.</p>

<h2>Up Next</h2>
<p>In the next article we will take a break from RubyMotion and dive into the world of Rails and ActiveAdmin.  I've been a contributor and maintainer of ActiveAdmin for quite a while and have written a gem, <a href="https://github.com/macfanatic/active_admin-state_machine">ActiveAdmin-StateMachine</a>, that we will examine in more detail.</p>

<p>ActiveAdmin-StateMachine is a great example of crafting a simple DSL for ActiveAdmin exposing functionality from another popular Rails gem, <a href="https://github.com/pluginaweek/state_machine">state_machine</a>.</p>]]></content><author><name>Matt</name></author><category term="ruby-motion" /><category term="ios" /><summary type="html"><![CDATA[In our last post we looked into the mechanics of creating a gem for RubyMotion, detailing the structure, spec coverage and basics of getting your files into the RubyMotion "pipeline" to be included in the compilation process.]]></summary></entry><entry><title type="html">Our first gem: Motion Launchpad</title><link href="https://www.madebylotus.com/blog/motion-launchpad/" rel="alternate" type="text/html" title="Our first gem: Motion Launchpad" /><published>2014-03-26T00:00:00+00:00</published><updated>2014-03-26T00:00:00+00:00</updated><id>https://www.madebylotus.com/blog/motion-launchpad</id><content type="html" xml:base="https://www.madebylotus.com/blog/motion-launchpad/"><![CDATA[<p>For our first real exercise in RubyMotion, there is no better place to start than writing our very first gem. We'll be creating a gem called 'motion-launchpad' that I'll open source and release for others to use, providing a simple schedule interface based on launch counts, for performing tasks in your app.</p>

<p>If you want to just view the gem directly and skip this tutorial (shame on you) you're welcome to <a href="https://github.com/macfanatic/motion-launchpad">check out the project on github</a>.</p>

<h2>Getting Started</h2>

<p>The first thing we are going to need to do is create a git repo and the basic gem structure for a RM project.  That means a folder with the following structure:</p>

<pre><code>- app
- Gemfile
- lib/
  - motion/
    - launchpad.rb
    - version.rb
  - motion-launchpad.rb
- motion-launchpad.gemspec
- spec/
</code></pre>

<p>A good way to get going is by installing the excellent <a href="https://github.com/svenfuchs/gem-release">gem release</a> command and using <code>gem bootstrap [gem_name]</code> as a starting point.  This applies to any Ruby gem and is not RM specific.</p>

<p>We'll start by creating the contents of our <code>lib/motion-launchpad.rb</code> file:</p>

<pre><code>unless defined?(Motion::Project::Config)
  raise "This file must be required within a RubyMotion project Rakefile."
end

lib_dir_path = File.dirname(File.expand_path(__FILE__))
Motion::Project::App.setup do |app|
  gem_files = Dir.glob(File.join(lib_dir_path, "motion/**/*.rb"))
  app.files.unshift(gem_files).flatten!
end
</code></pre>

<p>We'll also want to ensure that the project <code>Rakefile</code> is updated for a RM project.  At this point in our progress you can check out the <a href="https://github.com/macfanatic/motion-launchpad/commit/df2c0525898fd6664e0b5443df4c2f360c86fb0a">first commit</a> in our repo.</p>

<h2>Configuring RM App Delegate</h2>
<p>For our test suite to run, we'll need a UIApplication delegate that just returns true and doesn't worry about anything else, allowing our unit tests to run.</p>

<p>We're not including anything in the <code>app</code> directory as the contents of our gem, they're strictly there for the test suite to have a valid application to run against.</p>

<pre><code>class AppDelegate
  def application(application, didFinishLaunchingWithOptions:launchOptions)
    return true if RUBYMOTION_ENV == 'test'
  end
end
</code></pre>

<h2>Checking our Progress</h2>
<p>At this point, you should be able to just run <code>rake</code> from the Terminal and open the simulator with a black screen.  We have not created a window, rootViewController or anything else in our delegate and don't need to.</p>

<p>You can play in REPL and see that we have our modules and classes defined and that they are accessible.</p>

<pre><code>Motion::Launchpad.new
=&gt; #&lt;Motion::Launchpad:0x8e8a050&gt;
</code></pre>

<h2>Write our First Test</h2>
<p>Now that we have a basic structure and can use REPL, we're ready to write our first test and add our functionality.</p>

<pre><code>describe Motion::Launchpad do

  it "should return an instance" do
    Motion::Launchpad.configure.should.be.instance_of Motion::Launchpad
  end

  it "should return the same instance twice" do
    instance = Motion::Launchpad.configure
    instance.should.be.equal Motion::Launchpad.configure
  end
end
</code></pre>

<p>And the code that makes this test pass:</p>

<pre><code>module Motion
  class Launchpad

    class &lt;&lt; self
      attr_accessor :instance

      def configure(&amp;block)
        self.instance = new if instance.nil?
        instance
      end
    end
  end
end
</code></pre>

<p>You can read more about the rspec-ish syntax of <a href="https://github.com/alloy/MacBacon">MacBacon here</a>.</p>

<h2>Wrapping up</h2>
<p>For speed sake, we're just going to reference the <a href="https://github.com/macfanatic/motion-launchpad">project on github</a> for the finished gem.</p>

<p>Our finished product provides a nice and clean DSL for configuring events, such as:</p>

<pre><code>class AppDelegate
  def application(app, didFinishLaunchingWithOptions: options)

    setup_schedule
    Motion::Launchpad.run!
  end

  private

  def setup_schedule
    Motion::Launchpad.configure do |config|
      config.on :every do
        # maybe track app launch with analytics?
      end

      config.on 1 do
        # first launch, maybe show user a tutorial?
      end
    end
  end

  # You can call `configure` multiple times
  Motion::Launchpad.configure do |config|
    config.on 5 do
      # ask user to rate your app? Hate doing that, but best I could come up with
    end
  end
end
</code></pre>

<p>You can check out the complete <code>Motion::Launchpad::Schedule</code> class <a href="https://github.com/macfanatic/motion-launchpad/blob/master/lib/motion/launchpad/schedule.rb">here</a>, the <code>Motion::Launchpad::Event</code> class <a href="https://github.com/macfanatic/motion-launchpad/blob/master/lib/motion/launchpad/event.rb">here</a>, and the module showing some cooler meta programming methods in ruby <a href="https://github.com/macfanatic/motion-launchpad/blob/master/lib/motion/launchpad.rb">here</a>.</p>

<p>Releasing the gem is as easy as running <code>gem release --tag</code> from the directory, which I've already done. Enjoy and send a pull request if you have feedback, questions or improvements!</p>

<h2>Up Next</h2>
<p>In the next article we'll be taking a into meta-programming in RubyMotion and also be taking a look into the new <code>AVSpeechSynthesizer</code> class which debuted in iOS 7.</p>]]></content><author><name>Matt</name></author><category term="ruby-motion" /><category term="ios" /><summary type="html"><![CDATA[For our first real exercise in RubyMotion, there is no better place to start than writing our very first gem. We'll be creating a gem called 'motion-launchpad' that I'll open source and release for others to use, providing a simple schedule interface based on launch counts, for performing tasks in your app.]]></summary></entry></feed>