<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	
	xmlns:georss="http://www.georss.org/georss"
	xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#"
	>

<channel>
	<title>Technology &#8211; Cloud Nine Apps</title>
	<atom:link href="https://cloudnineapps.com/blogs/technology/feed/" rel="self" type="application/rss+xml" />
	<link>https://cloudnineapps.com</link>
	<description>Simplifying Technology</description>
	<lastBuildDate>Fri, 01 Jan 2021 02:02:32 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=5.4.16</generator>

<image>
	<url>https://i2.wp.com/cloudnineapps.com/wp-content/uploads/cropped-CloudNineApps_favicon-2.png?fit=32%2C32&#038;ssl=1</url>
	<title>Technology &#8211; Cloud Nine Apps</title>
	<link>https://cloudnineapps.com</link>
	<width>32</width>
	<height>32</height>
</image> 
<site xmlns="com-wordpress:feed-additions:1">136749396</site>	<item>
		<title>How To Design A Cross-Platform Mobile Application?</title>
		<link>https://cloudnineapps.com/blogs/technology/how-to-design-a-cross-platform-mobile-application/</link>
					<comments>https://cloudnineapps.com/blogs/technology/how-to-design-a-cross-platform-mobile-application/#respond</comments>
		
		<dc:creator><![CDATA[Nitin Patil]]></dc:creator>
		<pubDate>Fri, 01 Jan 2021 00:32:37 +0000</pubDate>
				<category><![CDATA[Technology]]></category>
		<guid isPermaLink="false">https://cloudnineapps.com/?p=4547</guid>

					<description><![CDATA[<p>How To Design A Cross-Platform Mobile Application? Mobile applications have taken a center stage for the past few years, and with the continuous improvements in mobile technology, we will see more and more applications embracing the mobile platform. However, as&#8230;&#160;<a href="https://cloudnineapps.com/blogs/technology/how-to-design-a-cross-platform-mobile-application/" class="more-link">Read More</a></p>
<p>The post <a rel="nofollow" href="https://cloudnineapps.com/blogs/technology/how-to-design-a-cross-platform-mobile-application/">How To Design A Cross-Platform Mobile Application?</a> appeared first on <a rel="nofollow" href="https://cloudnineapps.com">Cloud Nine Apps</a>.</p>
]]></description>
										<content:encoded><![CDATA[<h1>How To Design A Cross-Platform Mobile Application?</h1>
<p>Mobile applications have taken a center stage for the past few years, and with the continuous improvements in mobile technology, we will see more and more applications embracing the mobile platform. However, as an Architect and a developer, building a mobile application that will work across multiple platforms, and yet keep the code as sleek as possible is a daunting task. This was the challenge I faced when designing the <a href="https://cloudnineapps.com/products/mobile/shopping-guru/" target="_blank" rel="noopener noreferrer">Shopping Guru app</a> that helps in making the most out of your online purchases. In this article, I will cover how I went about making this cross-platform app with a high amount of code reusability.</p>
<h2>Cross-Platform Mobile Application Key Challenges</h2>
<p>For the sake of discussion, I&#8217;m summarizing the two core requirements for the Shopping Guru app so that we can understand the challenges and the key aspects of the design better.</p>
<ul>
<li>Do periodic checks to generate price drop and product availability alerts.</li>
<li>Facilitate goal-driven purchases by organizing these into projects. For example, create a project to set up a home theater.</li>
</ul>
<p>We briefly touched upon a couple of challenges in the introduction. But, let&#8217;s highlight the key challenges keeping the 2 dominant mobile platforms &#8211; <strong>iOS</strong> and <strong>Android</strong>.</p>
<ul>
<li>How to improve the code reusability? A big part of this question is how to use the same programming language? As we know, iOS supports Swift and Objective-C. Whereas, Android supports Java and Kotlin.</li>
<li>How to manage the app data in the cloud? For example, iOS offers iCloud functionality to store data. But, the moment you add Android into the mix, you do not want to have multiple ways of managing data in the cloud, especially, if your app has cloud-based processes that need access to this data. In the case of Shopping Guru, we needed access to the data to generate price drop alerts and to check whether a previously unavailable product is now available.</li>
<li>How to handle platform-specific interactions that originate &#8220;from&#8221; the common code? For example, when a price drop occurs generate a notification. The price drop check can be done in a platform-independent reusable code. However, the notification has to be generated using platform-specific features.</li>
<li>How to handle resources, such as externalized text and images?</li>
</ul>
<p>As you can see, if we don&#8217;t get hold of these aspects early on, the code could get quite messy.</p>
<h2>Application Architecture</h2>
<p><a href="https://i0.wp.com/cloudnineapps.com/wp-content/uploads/blogs/Technology/How_To_Design_A_Cross_Platform_Mobile_Application/ShoppingGuru_Architecture_Diagram.png?ssl=1"><img class="alignnone wp-image-4550 size-large" src="https://i0.wp.com/cloudnineapps.com/wp-content/uploads/blogs/Technology/How_To_Design_A_Cross_Platform_Mobile_Application/ShoppingGuru_Architecture_Diagram.png?resize=640%2C303&#038;ssl=1" alt="" width="640" height="303" srcset="https://i0.wp.com/cloudnineapps.com/wp-content/uploads/blogs/Technology/How_To_Design_A_Cross_Platform_Mobile_Application/ShoppingGuru_Architecture_Diagram.png?resize=1024%2C484&amp;ssl=1 1024w, https://i0.wp.com/cloudnineapps.com/wp-content/uploads/blogs/Technology/How_To_Design_A_Cross_Platform_Mobile_Application/ShoppingGuru_Architecture_Diagram.png?resize=300%2C142&amp;ssl=1 300w, https://i0.wp.com/cloudnineapps.com/wp-content/uploads/blogs/Technology/How_To_Design_A_Cross_Platform_Mobile_Application/ShoppingGuru_Architecture_Diagram.png?resize=768%2C363&amp;ssl=1 768w, https://i0.wp.com/cloudnineapps.com/wp-content/uploads/blogs/Technology/How_To_Design_A_Cross_Platform_Mobile_Application/ShoppingGuru_Architecture_Diagram.png?resize=320%2C151&amp;ssl=1 320w, https://i0.wp.com/cloudnineapps.com/wp-content/uploads/blogs/Technology/How_To_Design_A_Cross_Platform_Mobile_Application/ShoppingGuru_Architecture_Diagram.png?resize=640%2C302&amp;ssl=1 640w, https://i0.wp.com/cloudnineapps.com/wp-content/uploads/blogs/Technology/How_To_Design_A_Cross_Platform_Mobile_Application/ShoppingGuru_Architecture_Diagram.png?resize=360%2C170&amp;ssl=1 360w, https://i0.wp.com/cloudnineapps.com/wp-content/uploads/blogs/Technology/How_To_Design_A_Cross_Platform_Mobile_Application/ShoppingGuru_Architecture_Diagram.png?resize=720%2C340&amp;ssl=1 720w, https://i0.wp.com/cloudnineapps.com/wp-content/uploads/blogs/Technology/How_To_Design_A_Cross_Platform_Mobile_Application/ShoppingGuru_Architecture_Diagram.png?resize=1080%2C510&amp;ssl=1 1080w, https://i0.wp.com/cloudnineapps.com/wp-content/uploads/blogs/Technology/How_To_Design_A_Cross_Platform_Mobile_Application/ShoppingGuru_Architecture_Diagram.png?resize=800%2C378&amp;ssl=1 800w, https://i0.wp.com/cloudnineapps.com/wp-content/uploads/blogs/Technology/How_To_Design_A_Cross_Platform_Mobile_Application/ShoppingGuru_Architecture_Diagram.png?w=1168&amp;ssl=1 1168w" sizes="(max-width: 640px) 100vw, 640px" data-recalc-dims="1" /></a></p>
<p>After doing a few prototypes, I decided to split the application into 2 parts.</p>
<ol>
<li><strong>Mobile App</strong>: This is the app itself that contains primarily the user interaction code with basic logic.</li>
<li><strong>Cloud-based REST API</strong>: This handles the core business logic and provides additional capabilities to improve performance, such as caching and throttling calls to the Vendor API. The Vendor API is used to search and retrieve items.</li>
</ol>
<h3>Mobile Application Design</h3>
<p>The app comprises the following key components.</p>
<ul>
<li><strong>UI</strong>: The user interface and associated code. This layer is implemented separately for iOS and Android. The majority of the application code is written in Java. For Android, that&#8217;s pretty straight-forward. And, for iOS, I used <a href="https://multi-os-engine.org/" target="_blank" rel="noopener noreferrer">Multi-OS Engine</a>, which provides a Java runtime on iOS and makes it possible to write pretty much all of the code in Java. You still need to use Xcode to create the storyboard and generate the skeleton code (such as the controller header and implementation files). From that point, you can use Multi-OS Engine&#8217;s IDE plugins to generate Java artifacts. It&#8217;s free and the app performance was reasonable.</li>
<li><strong>Core</strong>: This component provides common business logic and maximum code reusability. It also handles common resources, such as externalized text, images, and configuration files. For outgoing calls from the Core module to platform-specific implementation, I decided to have specific interfaces that will be implemented by the respective platform-specific module. For example, both Android and iOS modules implement a <span style="font-family: courier new, courier, monospace;">NotificationManager</span> interface to provide notification management functionality. The UI code passes these module instances to the Core module at initialization time.</li>
<li><strong>Data Refresh</strong>: This component helps in fetching prices periodically, especially when the app is running in the foreground. Again, this is a reusable component.</li>
<li>The app uses the following custom libraries.
<ul>
<li><strong>NetworkMonitor</strong>: This is used for monitoring the network status, such as when the device is connected to WiFi versus when its connected to the cellular network. Depending on the type of connectivity, the app automatically switches network usage to avoid/minimize cellular data usage.</li>
<li><strong>AnalyticsManager</strong>: This is used to publish opt-in app usage metrics with user consent.</li>
</ul>
</li>
</ul>
<p>With this approach, I could significantly improve code and resource reusability.</p>
<h3>REST API Design</h3>
<p>The REST API acts as the brain of the solution and comprises the following key components.</p>
<ul>
<li>The API Gateway + Lambda combination provides the REST API layer that the app would use for needs like searching items and retrieving item prices. It interacts with the Vendor API behind the scenes on a need basis to fetch the latest data. Also, it can do price drop and product availability checks periodically.</li>
<li>DynamoDB is used to cache the frequently accessed data.</li>
<li>It also offers a platform-agnostic way of managing app data. In fact, for the most part, the cloud-based components do not even care about the device&#8217;s actual platform.</li>
</ul>
<p>Apart from resolving some of the challenges cited earlier, cloud-based components provide additional flexibility to add improvements and enhancements that do not require changes to the mobile app. This saves significant time to deliver enhancements to the end-users.</p>
<h2>Development Setup</h2>
<ul>
<li>I chose Android Studio as the core IDE since it is quite convenient for Android development. Besides, Multi-OS Engine offers a neat plugin to generate Java artifacts.</li>
<li>Xcode is used on a need basis when editing the storyboard and creating initial files. Also, it is used to publish builds to App Store Connect.</li>
<li>The build uses the gradle framework. Each module has a <span style="font-family: courier new, courier, monospace;">build.gradle</span> file.</li>
<li>For cloud deployment, I am using the AWS CloudFormation templates. This makes it easy to add more regions, as needed, literally in a matter of minutes!</li>
</ul>
<h2>Resources</h2>
<ul>
<li><a href="https://multi-os-engine.org/start/" target="_blank" rel="noopener noreferrer">Multi-OS Engine Getting Started Guide</a></li>
<li><a href="https://medium.com/swlh/how-to-design-applications-for-cloud-saas-88eab310ffad" target="_blank" rel="noopener noreferrer">How To Design Applications For Cloud (SaaS)?</a></li>
<li><a href="https://medium.com/swlh/how-to-create-a-multi-tier-stack-using-aws-cloudformation-f8f2685d27c6" target="_blank" rel="noopener noreferrer">How To Create a Multi-Tier Stack Using AWS CloudFormation?</a></li>
</ul>
<p>&nbsp;</p>
<p>Developing a cross-platform mobile application requires a focused approach to consistently identify platform-specific versus reusable code. Once you establish this basic rule of thumb, you can extend it further to identify components that can be reused across multiple applications (like the <span style="font-family: courier new, courier, monospace;">NetworkMonitor</span> and <span style="font-family: courier new, courier, monospace;">AnalyticsManager</span> in this article). And, I hope this article gives you a good enough starting point to build a prototype for your mobile application and see if it would meet your needs.</p>
<p>Happy developing!<br />
&#8211; Nitin</p>
<p><em>If you liked this post, you will find my <a href="https://cloudnineapps.com/courses/cloud-computing/aws-advanced-for-developers/" rel="noopener noreferrer">AWS Advanced For Developers</a> course helpful that focuses on many such best practices and techniques to design and deploy real-world applications in AWS.</em></p>
<p><div class="su-note"  style="border-color:#e5d6d4;border-radius:5px;-moz-border-radius:5px;-webkit-border-radius:5px;"><div class="su-note-inner su-u-clearfix su-u-trim" style="background-color:#fff0ee;border-color:#ffffff;color:#333333;border-radius:5px;-moz-border-radius:5px;-webkit-border-radius:5px;"></p>
<center>
<h4>Enhance your AWS skills with these hands-on courses for real-world deployments.</h4>
</center>
<p><div class="su-row"> <div class="su-column su-column-size-1-2"><div class="su-column-inner su-u-clearfix su-u-trim"></p>
<p><a href="https://cloudnineapps.com/courses/cloud-computing/aws-cloudformation-deep-dive/"><img class="aligncenter wp-image-3758 size-medium" src="https://i1.wp.com/cloudnineapps.com/wp-content/uploads/courses/Cloud_Computing/AWS_CloudFormation_Deep_Dive/AWS_CloudFormation_Deep_Dive_Logo.png?resize=192%2C108&#038;ssl=1" alt="" width="192" height="108" data-recalc-dims="1" /></a></p>
<center>Learn CloudFormation from concepts to hands-on examples.</center>
<p></div></div> <div class="su-column su-column-size-1-2"><div class="su-column-inner su-u-clearfix su-u-trim"></p>
<p><a href="https://cloudnineapps.com/courses/cloud-computing/aws-advanced-for-developers/"><img class="size-full wp-image-2068 aligncenter" src="https://i0.wp.com/cloudnineapps.com/wp-content/uploads/courses/Cloud_Computing/AWS_Advanced_For_Developers/AWS_Advanced_For_Developers_Logo_small.png?resize=192%2C108&#038;ssl=1" alt="" width="192" height="108" data-recalc-dims="1" /></a></p>
<center>Learn practical application development on AWS.</center>
<p></div></div> </div></div></div></p>
<p>&nbsp;</p>
<p><a class="a2a_button_facebook" href="https://www.addtoany.com/add_to/facebook?linkurl=https%3A%2F%2Fcloudnineapps.com%2Fblogs%2Ftechnology%2Fhow-to-design-a-cross-platform-mobile-application%2F&amp;linkname=How%20To%20Design%20A%20Cross-Platform%20Mobile%20Application%3F" title="Facebook" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_twitter" href="https://www.addtoany.com/add_to/twitter?linkurl=https%3A%2F%2Fcloudnineapps.com%2Fblogs%2Ftechnology%2Fhow-to-design-a-cross-platform-mobile-application%2F&amp;linkname=How%20To%20Design%20A%20Cross-Platform%20Mobile%20Application%3F" title="Twitter" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_linkedin" href="https://www.addtoany.com/add_to/linkedin?linkurl=https%3A%2F%2Fcloudnineapps.com%2Fblogs%2Ftechnology%2Fhow-to-design-a-cross-platform-mobile-application%2F&amp;linkname=How%20To%20Design%20A%20Cross-Platform%20Mobile%20Application%3F" title="LinkedIn" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_pinterest" href="https://www.addtoany.com/add_to/pinterest?linkurl=https%3A%2F%2Fcloudnineapps.com%2Fblogs%2Ftechnology%2Fhow-to-design-a-cross-platform-mobile-application%2F&amp;linkname=How%20To%20Design%20A%20Cross-Platform%20Mobile%20Application%3F" title="Pinterest" rel="nofollow noopener" target="_blank"></a><a class="a2a_dd a2a_counter addtoany_share_save addtoany_share" href="https://www.addtoany.com/share#url=https%3A%2F%2Fcloudnineapps.com%2Fblogs%2Ftechnology%2Fhow-to-design-a-cross-platform-mobile-application%2F&#038;title=How%20To%20Design%20A%20Cross-Platform%20Mobile%20Application%3F" data-a2a-url="https://cloudnineapps.com/blogs/technology/how-to-design-a-cross-platform-mobile-application/" data-a2a-title="How To Design A Cross-Platform Mobile Application?"></a></p><p>The post <a rel="nofollow" href="https://cloudnineapps.com/blogs/technology/how-to-design-a-cross-platform-mobile-application/">How To Design A Cross-Platform Mobile Application?</a> appeared first on <a rel="nofollow" href="https://cloudnineapps.com">Cloud Nine Apps</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://cloudnineapps.com/blogs/technology/how-to-design-a-cross-platform-mobile-application/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">4547</post-id>	</item>
		<item>
		<title>How To Approach The Internal Documentation Of Your Software Projects?</title>
		<link>https://cloudnineapps.com/blogs/technology/how-to-approach-the-internal-documentation-of-your-software-projects/</link>
					<comments>https://cloudnineapps.com/blogs/technology/how-to-approach-the-internal-documentation-of-your-software-projects/#respond</comments>
		
		<dc:creator><![CDATA[Nitin Patil]]></dc:creator>
		<pubDate>Wed, 05 Feb 2020 11:25:35 +0000</pubDate>
				<category><![CDATA[Technology]]></category>
		<guid isPermaLink="false">https://cloudnineapps.com/?p=4452</guid>

					<description><![CDATA[<p>How To Approach The Internal Documentation Of Your Software Projects? Many Software Architects, Developers, and Leads face this question for their projects &#8211; whether they should document their projects? If yes, how much? No, we are not talking about the&#8230;&#160;<a href="https://cloudnineapps.com/blogs/technology/how-to-approach-the-internal-documentation-of-your-software-projects/" class="more-link">Read More</a></p>
<p>The post <a rel="nofollow" href="https://cloudnineapps.com/blogs/technology/how-to-approach-the-internal-documentation-of-your-software-projects/">How To Approach The Internal Documentation Of Your Software Projects?</a> appeared first on <a rel="nofollow" href="https://cloudnineapps.com">Cloud Nine Apps</a>.</p>
]]></description>
										<content:encoded><![CDATA[<h1>How To Approach The Internal Documentation Of Your Software Projects?</h1>
<p>Many Software Architects, Developers, and Leads face this question for their projects &#8211; whether they should document their projects? If yes, how much? No, we are not talking about the required documentation that almost every software project needs, like the customer-facing documentation. By &#8220;internal documentation&#8221;, I am talking about technical documentation like Architecture and Design documents. In this post, we will discuss how to approach such internal documentation.</p>
<h2>Why Document Your Project?</h2>
<p>This is the most important question. Simply put, what is your intent behind documentation? Are you planning to use it as a tool that can help you build more quality software or are you simply putting together some artifacts for the sake of it? And, this is not just a question for the Architects and Designers, but also the entire team. After all, when you are investing time and effort in the documentation, most of the team should benefit from it. Projects that take the approach of using documentation as a tool to build more quality software not only benefit from it, but these have more useful documentation.</p>
<h3>Pros of Technical Documentation</h3>
<p>The following are some of the common benefits of having technical documentation.</p>
<ul>
<li>It helps validate the approach, design, and implementation.</li>
<li>It can help establish the core concepts and avoid common misunderstandings.</li>
<li>The same artifact can benefit multiple aspects of the software life cycle. Remember that the same document may be consumed in different ways by different team members. For example, while a developer may look at it for their implementation, a quality assurance team member can use it for writing their test cases.</li>
<li>It can significantly help in ramping up new team members who join the project later.</li>
<li>It establishes transparency in sharing information that could benefit the entire team.</li>
<li>By keeping it up-to-date, it can help avoid redundant communication.</li>
</ul>
<h3>Cons of Technical Documentation</h3>
<p>Of course, documentation comes at a cost. These are not necessarily cons of documentation but are associated with it.</p>
<ul>
<li>It requires a time investment.</li>
<li>It runs the risk of getting outdated as the project progresses. This is often the case when documentation is produced for the sake of it but is not being used for anything meaningful.</li>
</ul>
<p>So, as you can see, it all boils down to the point about the purpose of the documentation.</p>
<h2>How To Approach Technical Documentation?</h2>
<p>I am a big believer in using documentation as a tool to deliver quality software. Hence, I like to invest time to make it more meaningful. Here are some useful tips that I like to consider for the documentation.</p>
<ul>
<li><strong>Establish the core concepts</strong>: Having a simple section like glossary is good. But, often you, as a designer, may have some core concepts that you would like the team to understand. Just like in the business world, there are key driving factors, there are certain core concepts that are key to the functioning of your application. For example, specific data model elements, algorithmic constructs that can help the team understand how the system is architected and what was the thought process behind it. It is interesting to come back to this section a year later and find whether these are still applicable!</li>
<li><strong>Organize effectively</strong>: Avoid clubbing multiple types of content in the same section. It not only makes it harder to understand, but it could create confusion and make it difficult for other team members to extract information. For example, instead of including data model, deployment, performance altogether, consider splitting these into separate sections. For more complex projects, it may be worth having dedicated documentation for certain components/services and having links to these from the overall project-level documentation. This way, team members focused on those components can find information more easily. It would also make it easier to delegate ownership of maintaining this documentation.</li>
<li><strong>Be concise</strong>: The good thing when you write is you can try to articulate your thoughts as concisely as possible. I like to use bullets over paragraphs especially when the topic is dense.</li>
<li><strong>Keep it simple</strong>: That goes without saying. Avoid the use of complex words and jargon that are not helpful. Of course, if some complex terms are vital from an understanding perspective, use these, but make sure that these have been clarified.</li>
</ul>
<h3>What Should I Include In My Software Design Document?</h3>
<p>A Software Design Document (a.k.a. SDD) is a <strong>living commentary</strong> of your project. The following are some key pieces of information that I would like to include in an SDD.</p>
<p><span style="text-decoration: underline;">Note</span>: I talk about multiple diagrams as each diagram helps in analyzing the application differently, and collectively these help in gaining a more complete picture.</p>
<ul>
<li><strong>Glossary</strong>: This captures the key terms that consumers of the documentation should be familiar with. For example, any important abbreviations you are using throughout the documentation.</li>
<li><strong>Architecture Overview</strong>: An overview of the System Architecture. This is a very high-level blueprint of your application, such as an n-tier architecture that will typically not change much over the life span of the application.</li>
<li><strong>Key Concepts</strong>: We talked about these earlier in this post. This section should cover the key concepts and how these work together to make your application function. Also, coming up with meaningful names for these concepts can often help you convey a specific point easily as opposed to always explain what you are talking about.</li>
<li><strong>High-Level Design</strong>: This should capture the overall system design, key components, and their dependencies. Often a <a href="https://en.wikipedia.org/wiki/Component_diagram" target="_blank" rel="noopener noreferrer">UML Component Diagram</a> can come in handy here. The idea here is to give sufficient guidance to individual components&#8217; leads to understanding how they fit in the overall picture. At the same time, giving them at least some level of flexibility in their specific component design, as appropriate.</li>
<li><strong>Deployment Diagram</strong>: If there is one diagram you must-have in almost every SDD, then this is the one. It helps in establishing key details like deployment nodes, which components will run on these, ports exposed, protocols used, and the direction of communication. This is often accomplished via a <a href="https://en.wikipedia.org/wiki/Deployment_diagram" target="_blank" rel="noopener noreferrer">UML Deployment Diagram</a>.</li>
<li><strong>Data Model</strong>: This is at the heart of many enterprise applications. At a minimum, this should include key entities and their relationships. Often, this will be the input for the database implementation. <a href="https://en.wikipedia.org/wiki/Entity%E2%80%93relationship_model" target="_blank" rel="noopener noreferrer">Entity-relationship Diagrams</a> or ER Diagrams are often used to capture this.</li>
<li><strong>Class Diagrams</strong>: Having one or more <a href="https://en.wikipedia.org/wiki/Class_diagram" target="_blank" rel="noopener noreferrer">Class Diagrams</a> that cover the key classes and their relationships can help the team members build a better understanding. Here, you should focus on core classes that contain business logic and avoid cluttering with classes that are only relevant from the data perspective. Those would typically have been covered in the Data Model anyway.</li>
<li><strong>Interaction Model</strong>: This helps in understanding component interaction in chronological order. This is often captured via <a href="https://en.wikipedia.org/wiki/Sequence_diagram" target="_blank" rel="noopener noreferrer">UML Sequence Diagrams</a>.</li>
<li><strong>Compatibility Matrix</strong>: If your application has specific compatibility requirements, such as operating system version, library versions, etc it is worth capturing these. These details are extremely important for testing as well as for customer documentation.</li>
<li><strong>Dependencies</strong>: Any third-party libraries or other dependencies that your application requires. This is important for technical aspects like builds. It is also important for legal because, at times, developers may pick up a library that may not be the best choice from a legal standpoint. So, the sooner you can jot down this list and get clearance from stakeholders, the better.</li>
<li><strong>Performance and Sizing</strong>: What are the sizing recommendations and what performance objectives and criteria do the design cater to? The answer may not be always simple and may require a few iterations. However, the point is to have at least some level of guidance and tweak it based on learnings.</li>
<li><strong>Security</strong>: Although other sections may touch upon it, it is important to make this section and address any specific security measures implemented in the application. This could be the use of secured protocols, network configurations, even some operational aspects like regular scanning and detection. This is not an operational document but should give guidance to the Operations team so that they know what measures they should have in place to secure the application.</li>
<li><strong>Enterprise Readiness</strong>: This covers various other aspects like high availability, accessibility, etc.</li>
<li><strong>Install and Upgrade</strong>: This section should cover considerations for fresh installation and upgrades. It is worth including any specific design considerations, such as the backward compatibility of the data model.</li>
<li><strong>Revision History</strong>: Finally, a section that contains the timestamp, the change author and a quick comment describing the change.</li>
</ul>
<p>The following are some not so commonly used sections. But, I like to use these in my SDDs as this helps bring in a lot more clarity and enabling various teams (including the support teams).</p>
<ul>
<li><strong>Developer Notes</strong>: This section contains useful tips and information on the code-level information. For example, how multi-threading should be used for a specific component, use of tokens to identify a request end-to-end, etc.</li>
<li><strong>Logging</strong>: Include specific patterns for details like understanding the flow, identifying a request, etc. Sample log snippets also can be extremely helpful.</li>
<li><strong>Troubleshooting</strong>: Provide information on how to troubleshoot the application. This should cover things like log locations, how to analyze logs, other system parameters to analyze, etc. If you can take a few key scenarios, that can also be helpful.</li>
<li><strong>Common Problems and Solutions</strong>: This is useful to capture the commonly observed issues and provide guidance on how to resolve them. As you can imagine, sections like these will continue to grow and can provide extremely useful information for the support knowledgebase.</li>
</ul>
<p>There are a lot of areas to cover and not everything needs to be done on day one. However, having place holder sections and updating these over time can help you in building useful documentation.</p>
<h2>Handling Future Updates To Documentation</h2>
<p>Keeping documentation up-to-date is critical to ensure it continues to be an effective tool. Good checkpoints when you can ensure that the documentation is updated.</p>
<ul>
<li>Any important technical assumptions are introduced or changed</li>
<li>Agile iteration boundaries</li>
<li>When new features are developed</li>
<li>When new major release begins</li>
</ul>
<h2>Conclusion</h2>
<p>As we can see from this post, technical or internal documentation can be a quite effective tool in building quality software and keeping teams in sync. Hence, instead of looking at it as an overhead, look at it as an investment that will pay you off as you embark on your software journey.</p>
<p>Happy developing!<br />
&#8211; Nitin</p>
<p><div class="su-note"  style="border-color:#e5d6d4;border-radius:5px;-moz-border-radius:5px;-webkit-border-radius:5px;"><div class="su-note-inner su-u-clearfix su-u-trim" style="background-color:#fff0ee;border-color:#ffffff;color:#333333;border-radius:5px;-moz-border-radius:5px;-webkit-border-radius:5px;"></p>
<center>
<h4>Enhance your AWS skills with these hands-on courses for real-world deployments.</h4>
</center>
<p><div class="su-row"> <div class="su-column su-column-size-1-2"><div class="su-column-inner su-u-clearfix su-u-trim"></p>
<p><a href="https://cloudnineapps.com/courses/cloud-computing/aws-cloudformation-deep-dive/"><img class="aligncenter wp-image-3758 size-medium" src="https://i1.wp.com/cloudnineapps.com/wp-content/uploads/courses/Cloud_Computing/AWS_CloudFormation_Deep_Dive/AWS_CloudFormation_Deep_Dive_Logo.png?resize=192%2C108&#038;ssl=1" alt="" width="192" height="108" data-recalc-dims="1" /></a></p>
<center>Learn CloudFormation from concepts to hands-on examples.</center>
<p></div></div> <div class="su-column su-column-size-1-2"><div class="su-column-inner su-u-clearfix su-u-trim"></p>
<p><a href="https://cloudnineapps.com/courses/cloud-computing/aws-advanced-for-developers/"><img class="size-full wp-image-2068 aligncenter" src="https://i0.wp.com/cloudnineapps.com/wp-content/uploads/courses/Cloud_Computing/AWS_Advanced_For_Developers/AWS_Advanced_For_Developers_Logo_small.png?resize=192%2C108&#038;ssl=1" alt="" width="192" height="108" data-recalc-dims="1" /></a></p>
<center>Learn practical application development on AWS.</center>
<p></div></div> </div></div></div></p>
<p>&nbsp;</p>
<p><a class="a2a_button_facebook" href="https://www.addtoany.com/add_to/facebook?linkurl=https%3A%2F%2Fcloudnineapps.com%2Fblogs%2Ftechnology%2Fhow-to-approach-the-internal-documentation-of-your-software-projects%2F&amp;linkname=How%20To%20Approach%20The%20Internal%20Documentation%20Of%20Your%20Software%20Projects%3F" title="Facebook" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_twitter" href="https://www.addtoany.com/add_to/twitter?linkurl=https%3A%2F%2Fcloudnineapps.com%2Fblogs%2Ftechnology%2Fhow-to-approach-the-internal-documentation-of-your-software-projects%2F&amp;linkname=How%20To%20Approach%20The%20Internal%20Documentation%20Of%20Your%20Software%20Projects%3F" title="Twitter" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_linkedin" href="https://www.addtoany.com/add_to/linkedin?linkurl=https%3A%2F%2Fcloudnineapps.com%2Fblogs%2Ftechnology%2Fhow-to-approach-the-internal-documentation-of-your-software-projects%2F&amp;linkname=How%20To%20Approach%20The%20Internal%20Documentation%20Of%20Your%20Software%20Projects%3F" title="LinkedIn" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_pinterest" href="https://www.addtoany.com/add_to/pinterest?linkurl=https%3A%2F%2Fcloudnineapps.com%2Fblogs%2Ftechnology%2Fhow-to-approach-the-internal-documentation-of-your-software-projects%2F&amp;linkname=How%20To%20Approach%20The%20Internal%20Documentation%20Of%20Your%20Software%20Projects%3F" title="Pinterest" rel="nofollow noopener" target="_blank"></a><a class="a2a_dd a2a_counter addtoany_share_save addtoany_share" href="https://www.addtoany.com/share#url=https%3A%2F%2Fcloudnineapps.com%2Fblogs%2Ftechnology%2Fhow-to-approach-the-internal-documentation-of-your-software-projects%2F&#038;title=How%20To%20Approach%20The%20Internal%20Documentation%20Of%20Your%20Software%20Projects%3F" data-a2a-url="https://cloudnineapps.com/blogs/technology/how-to-approach-the-internal-documentation-of-your-software-projects/" data-a2a-title="How To Approach The Internal Documentation Of Your Software Projects?"></a></p><p>The post <a rel="nofollow" href="https://cloudnineapps.com/blogs/technology/how-to-approach-the-internal-documentation-of-your-software-projects/">How To Approach The Internal Documentation Of Your Software Projects?</a> appeared first on <a rel="nofollow" href="https://cloudnineapps.com">Cloud Nine Apps</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://cloudnineapps.com/blogs/technology/how-to-approach-the-internal-documentation-of-your-software-projects/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">4452</post-id>	</item>
		<item>
		<title>Code Coverage Is Not Enough</title>
		<link>https://cloudnineapps.com/blogs/technology/code-coverage-is-not-enough/</link>
					<comments>https://cloudnineapps.com/blogs/technology/code-coverage-is-not-enough/#respond</comments>
		
		<dc:creator><![CDATA[Nitin Patil]]></dc:creator>
		<pubDate>Fri, 07 Jun 2019 13:02:01 +0000</pubDate>
				<category><![CDATA[Technology]]></category>
		<guid isPermaLink="false">https://cloudnineapps.com/?p=3580</guid>

					<description><![CDATA[<p>Code Coverage Is Not Enough Code Coverage has been an important quality metric to measure the robustness of the code for several years now. It is part of the release criteria for many organizations from development to quality assurance and,&#8230;&#160;<a href="https://cloudnineapps.com/blogs/technology/code-coverage-is-not-enough/" class="more-link">Read More</a></p>
<p>The post <a rel="nofollow" href="https://cloudnineapps.com/blogs/technology/code-coverage-is-not-enough/">Code Coverage Is Not Enough</a> appeared first on <a rel="nofollow" href="https://cloudnineapps.com">Cloud Nine Apps</a>.</p>
]]></description>
										<content:encoded><![CDATA[<h1>Code Coverage Is Not Enough</h1>
<p>Code Coverage has been an important quality metric to measure the robustness of the code for several years now. It is part of the release criteria for many organizations from development to quality assurance and, at times, even for the senior management team. In fact, it is not unusual that several teams strive for a higher code coverage metric anywhere from 60-80% as the bare minimum threshold for a release. With all that said it is important to understand that code coverage by itself is not as useful. For example, even if you had a very high code coverage of 80% and the majority of your core logic is in that other 20% of the code, chances are high the code could have some serious defects that may slip into the release. What is worse that even if the code coverage were 100% that does not mean all the functionality has been covered! There could be important functional scenarios that might have been missed in the production code and the unit tests, which may again lead to an incomplete release. Don&#8217;t get me wrong. Code Coverage is important, but not when it is looked in isolation. When combined with good functional unit tests, it can be extremely useful. And, that&#8217;s what we will cover in this article.</p>
<h2>Let&#8217;s See Some Code</h2>
<p>What would be a better way of showing other than using an actual code? So, let&#8217;s take a barebone Inventory Management example that offers the following basic functionality.<br />
<span style="text-decoration: underline;">Note</span>: The functionality and logic are intentionally kept simple so that we can focus on the main topic of this article.</p>
<ul>
<li>Get product details.</li>
<li>Get the price of a product.</li>
<li>Get the price change of a product.</li>
</ul>
<p>In order to support this functionality, we have 3 classes.</p>
<ul>
<li><strong>InventoryManagerDelegate</strong>: This contains the core business logic and will typically be used by another tier or layer, such as a REST API layer to offer the inventory management APIs.</li>
<li><strong>InventoryDAO</strong>: The Data Access Object to handle all database interactions, such as adding and retrieving a product.</li>
<li><strong>Product</strong>: The model or data object to store product information (a.k.a. POJO (Plain Old Java Object) in the Java lingo).</li>
</ul>
<p>Here is the source code for these classes.</p>
<pre class="lang:default decode:true" title="InventoryManagerDelegate.java">package com.cloudnineapps.samples;

/**
 * The inventory manager delegate.
 */
public class InventoryManagerDelegate {

  /** The DAO. */
  private InventoryDAO dao;
  
  
  /** Initializes the delegate. */
  public void init() throws Exception {
    dao = new InventoryDAO();
    dao.createSchema();
  }

  /**
   * Returns the details for the product with the supplied title.
   * 
   * @param title The product title.
   */
  public Product getProductDetails(String title) throws Exception {
    Product product = null;
    try {
      product = dao.getProduct(title);
    }
    catch(IllegalArgumentException ex) {
      // Product not found, handle gracefully
      product = new Product();
    }
    
    return product;
  }
  
  /**
   * Returns the price for the specified product.
   * 
   * @param title The product title.
   */
  public double getProductPrice(String title) throws Exception {
    // NOTE: This code does not gracefully handle the product not found scenario
    Product product = dao.getProduct(title);
    return product.getPrice();
  }
  
  /**
   * Returns the price change for the specified product from the current price. A negative value
   * indicates decrease in price and a positive value indicates an increase.
   * 
   * @param title The product title.
   * @param currentPrice The current price of the product.
   */
  public double getProductPriceChange(String title, double currentPrice) throws Exception {
    // NOTE: This code does not gracefully handle the product not found scenario
    double price = 0;
    try {
      Product product = dao.getProduct(title);
      price = product.getPrice();
    }
    catch(Exception ex) {
    }
    return (currentPrice - price);
  }
  
  /**
   * Adds a product with the supplied details.
   * 
   * @param id The product ID.
   * @param title The product title.
   * @param description The product description.
   * @param price The product price.
   */
  public void addProduct(int id, String title, String description, double price) throws Exception {
    dao.addProduct(id, title, description, price);
  }
}</pre>
<pre class="lang:default decode:true" title="InventoryDAO.java">package com.cloudnineapps.samples;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;

/**
 * The inventory DAO.
 */
public class InventoryDAO {
  
  /** The database name. */
  private static final String DB_NAME = "inventory.db";

  /** Returns the current database connection. */
  public Connection getConnection() throws Exception {
    return DriverManager.getConnection(String.format("jdbc:hsqldb:file:data/%s", DB_NAME), "SA", "");
  }
  
  /** Closes the supplied connection. */
  public void closeConnection(Connection con) throws Exception {
    if (con != null) {
      con.close();
    }
  }
  
  /** Creates the database schema. */
  public void createSchema() throws Exception {
    String[] sqls = {
      "CREATE TABLE IF NOT EXISTS product("
        + "id INT NOT NULL,"
        + "title VARCHAR(255) NOT NULL,"
        + " description VARCHAR(255) NOT NULL,"
        + " price REAL NOT NULL,"
        + " PRIMARY KEY(id));",
      "CREATE UNIQUE INDEX IF NOT EXISTS idx_product_title ON product(title);"
    };
    Connection con = getConnection();
    try {
      for(int i = 0; i &lt; sqls.length; i++) {
        Statement stmt = con.createStatement();
        stmt.execute(sqls[i]);
        stmt.close();
      }
    }
    finally {
      closeConnection(con);
    }
  }
  
  /**
   * Returns the product with the specified title, if any. Otherwise, null.
   * 
   * @param title The product title.
   * 
   * @exception IllegalArgumentException when the title was not found.
   */
  public Product getProduct(String title) throws Exception {
    Product product = null;
    String sql = "SELECT id, description, price FROM product WHERE title=?";
    Connection con = getConnection();
    PreparedStatement stmt = null;
    ResultSet rs = null;
    try {
      stmt = con.prepareStatement(sql);
      stmt.setString(1, title);
      rs = stmt.executeQuery();
      if (rs.next()) {
        int i = 1;
        int id = rs.getInt(i++);
        String description = rs.getString(i++);
        double price = rs.getDouble(i++);
        product = new Product();
        product.setId(id);
        product.setTitle(title);
        product.setDescription(description);
        product.setPrice(price);
      }
      else {
        // Product not found
        throw new IllegalArgumentException(String.format("A product with title '%s' not found.", title));
      }
    }
    finally {
      rs.close();
      stmt.close();
      closeConnection(con);
    }
        
    return product;
  }
  
  /**
   * Adds a product with the supplied details.
   * 
   * @param id The product ID.
   * @param title The product title.
   * @param description The product description.
   * @param price The product price.
   */
  public void addProduct(int id, String title, String description, double price) throws Exception {
    String sql = "INSERT INTO product(id, title, description, price) VALUES(?, ?, ?, ?)";
    try {
      if (getProduct(title) != null) {
        // Product already exists, for now skip processing
        return;
      }
    }
    catch(IllegalArgumentException ex) {}
    Connection con = getConnection();
    PreparedStatement stmt = null;
    try {
      stmt = con.prepareStatement(sql);
      int i = 1;
      stmt.setInt(i++, id);
      stmt.setString(i++, title);
      stmt.setString(i++, description);
      stmt.setDouble(i++, price);
      stmt.executeUpdate();
    }
    finally {
      stmt.close();
      closeConnection(con);
    }
  }
}
</pre>
<pre class="lang:default decode:true" title="Product.java">package com.cloudnineapps.samples;

/**
 * The product.
 */
public class Product {
  
  private int id;
  private String title;
  private String description;
  private double price;

  public int getId() {
    return id;
  }
  
  public void setId(int id) {
    this.id = id;
  }
  
  public String getTitle() {
    return title;
  }
  
  public void setTitle(String title) {
    this.title = title;
  }
  
  public String getDescription() {
    return description;
  }
  
  public void setDescription(String description) {
    this.description = description;
  }
  
  public double getPrice() {
    return price;
  }
  
  public void setPrice(double price) {
    this.price = price;
  }
}
</pre>
<p>Most of the code is self-explanatory. But, here are some highlights.</p>
<ul>
<li>The delegate initializes the DAO, which initializes the schema.</li>
<li>The delegate offers 3 methods that correspond to the 3 core use cases &#8211; <span style="font-family: courier new, courier, monospace;">getProductDetails()</span>, <span style="font-family: courier new, courier, monospace;">getProductPrice()</span> and <span style="font-family: courier new, courier, monospace;">getProductPriceChange()</span>.</li>
<li>You may have already noticed in the comments in the <span style="font-family: courier new, courier, monospace;">getProductPrice()</span> and <span style="font-family: courier new, courier, monospace;">getProductPriceChange()</span> method bodies that these do not handle the scenario when the product with the specified title is not found.</li>
</ul>
<h2>Let&#8217;s Unit Test This Code</h2>
<p><span style="text-decoration: underline;">Note</span>: There are many types of unit testing methodologies used from unit testing each layer to sub-integration style tests. For the purpose of this article, this distinction is not relevant as the points covered can be applied to multiple types of unit tests.</p>
<pre class="lang:default decode:true" title="TestInventoryManagerDelegate.java">package com.cloudnineapps.samples;

import junit.framework.TestCase;

/**
 * The unit tests for Inventory Manager delegate.
 */
public class TestInventoryManagerDelegate extends TestCase {

  /** The delegate. */
  private InventoryManagerDelegate delegate;
  
  
  public void setUp() throws Exception {
    delegate = new InventoryManagerDelegate();
    delegate.init();
    delegate.addProduct(1, "TV", "4K TV", 299.99);
    delegate.addProduct(2, "Receiver", "4K Receiver", 149.99);
    delegate.addProduct(3, "Audio System", "Surround Sound", 199.99);
  }
  
  /** Tests retrieval of product details successfully. */
  public void testGetProductDetailsSuccess() throws Exception {
    // Initialize
    String title = "TV";
    
    // Execute
    Product product = delegate.getProductDetails(title);
    
    // Validate
    assertEquals("Incorrect title.", title, product.getTitle());
  }
  
  /** Tests retrieval of product details when title does not exist. */
  public void testGetProductDetailsWithNonExistingTitle() throws Exception {
    // Initialize
    String title = "Remote";
    
    // Execute
    Product product = delegate.getProductDetails(title);
    
    // Validate
    assertNull("The title must be null.", product.getTitle());
  }
  
  /** Tests retrieval of product price successfully. */
  public void testGetProductPriceSuccess() throws Exception {
    // Initialize
    String title = "TV";
    double expectedPrice = 299.99;
    
    // Execute
    double price = delegate.getProductPrice(title);
    
    // Validate
    assertEquals("Incorrect price.", expectedPrice, price);
  }
  
  /** Tests retrieval of product price change successfully. */
  public void testGetProductPriceChangeSuccess() throws Exception {
    // Initialize
    String title = "TV";
    double currentPrice = 249.99;
    double expectedPriceChange = -50;
    
    // Execute
    double price = delegate.getProductPriceChange(title, currentPrice);
    
    // Validate
    assertEquals("Incorrect price change.", expectedPriceChange, price);
  }
}
</pre>
<p>Fairly straight-forward, right?</p>
<ul>
<li>The first thing to notice is these unit tests are written with functionality in mind. For example, successful retrieval of product details and when product title was not found.</li>
<li>A couple of functional tests for getting product price and price change have been intentionally left out to show why simply relying on code coverage is not a good idea. For example, the test to check the behavior when a product is not found while getting price.</li>
</ul>
<h2>Let&#8217;s Generate The Code Coverage</h2>
<p>Before we can talk about why code coverage is not enough, let&#8217;s generate it for this code. Here are some screenshots.</p>
<p><strong>Overall Code Coverage</strong></p>
<p><a href="https://i1.wp.com/cloudnineapps.com/wp-content/uploads/blogs/Technology/Code_Coverage_Is_Not_Enough/Code_Coverage_Report.png?ssl=1"><img class="alignnone wp-image-3584 size-large" src="https://i1.wp.com/cloudnineapps.com/wp-content/uploads/blogs/Technology/Code_Coverage_Is_Not_Enough/Code_Coverage_Report.png?resize=640%2C63&#038;ssl=1" alt="" width="640" height="63" srcset="https://i1.wp.com/cloudnineapps.com/wp-content/uploads/blogs/Technology/Code_Coverage_Is_Not_Enough/Code_Coverage_Report.png?resize=1024%2C100&amp;ssl=1 1024w, https://i1.wp.com/cloudnineapps.com/wp-content/uploads/blogs/Technology/Code_Coverage_Is_Not_Enough/Code_Coverage_Report.png?resize=300%2C29&amp;ssl=1 300w, https://i1.wp.com/cloudnineapps.com/wp-content/uploads/blogs/Technology/Code_Coverage_Is_Not_Enough/Code_Coverage_Report.png?resize=768%2C75&amp;ssl=1 768w, https://i1.wp.com/cloudnineapps.com/wp-content/uploads/blogs/Technology/Code_Coverage_Is_Not_Enough/Code_Coverage_Report.png?resize=320%2C31&amp;ssl=1 320w, https://i1.wp.com/cloudnineapps.com/wp-content/uploads/blogs/Technology/Code_Coverage_Is_Not_Enough/Code_Coverage_Report.png?resize=640%2C63&amp;ssl=1 640w, https://i1.wp.com/cloudnineapps.com/wp-content/uploads/blogs/Technology/Code_Coverage_Is_Not_Enough/Code_Coverage_Report.png?resize=360%2C35&amp;ssl=1 360w, https://i1.wp.com/cloudnineapps.com/wp-content/uploads/blogs/Technology/Code_Coverage_Is_Not_Enough/Code_Coverage_Report.png?resize=720%2C70&amp;ssl=1 720w, https://i1.wp.com/cloudnineapps.com/wp-content/uploads/blogs/Technology/Code_Coverage_Is_Not_Enough/Code_Coverage_Report.png?resize=1080%2C106&amp;ssl=1 1080w, https://i1.wp.com/cloudnineapps.com/wp-content/uploads/blogs/Technology/Code_Coverage_Is_Not_Enough/Code_Coverage_Report.png?resize=800%2C78&amp;ssl=1 800w, https://i1.wp.com/cloudnineapps.com/wp-content/uploads/blogs/Technology/Code_Coverage_Is_Not_Enough/Code_Coverage_Report.png?resize=1280%2C125&amp;ssl=1 1280w, https://i1.wp.com/cloudnineapps.com/wp-content/uploads/blogs/Technology/Code_Coverage_Is_Not_Enough/Code_Coverage_Report.png?w=1697&amp;ssl=1 1697w" sizes="(max-width: 640px) 100vw, 640px" data-recalc-dims="1" /></a></p>
<p><strong>Delegate Code Coverage</strong></p>
<p><a href="https://i0.wp.com/cloudnineapps.com/wp-content/uploads/blogs/Technology/Code_Coverage_Is_Not_Enough/Code_Coverage_Delegate.png?ssl=1"><img class="alignnone wp-image-3585 size-large" src="https://i0.wp.com/cloudnineapps.com/wp-content/uploads/blogs/Technology/Code_Coverage_Is_Not_Enough/Code_Coverage_Delegate.png?resize=640%2C894&#038;ssl=1" alt="" width="640" height="894" srcset="https://i0.wp.com/cloudnineapps.com/wp-content/uploads/blogs/Technology/Code_Coverage_Is_Not_Enough/Code_Coverage_Delegate.png?resize=733%2C1024&amp;ssl=1 733w, https://i0.wp.com/cloudnineapps.com/wp-content/uploads/blogs/Technology/Code_Coverage_Is_Not_Enough/Code_Coverage_Delegate.png?resize=215%2C300&amp;ssl=1 215w, https://i0.wp.com/cloudnineapps.com/wp-content/uploads/blogs/Technology/Code_Coverage_Is_Not_Enough/Code_Coverage_Delegate.png?resize=768%2C1072&amp;ssl=1 768w, https://i0.wp.com/cloudnineapps.com/wp-content/uploads/blogs/Technology/Code_Coverage_Is_Not_Enough/Code_Coverage_Delegate.png?resize=320%2C447&amp;ssl=1 320w, https://i0.wp.com/cloudnineapps.com/wp-content/uploads/blogs/Technology/Code_Coverage_Is_Not_Enough/Code_Coverage_Delegate.png?resize=640%2C894&amp;ssl=1 640w, https://i0.wp.com/cloudnineapps.com/wp-content/uploads/blogs/Technology/Code_Coverage_Is_Not_Enough/Code_Coverage_Delegate.png?resize=360%2C503&amp;ssl=1 360w, https://i0.wp.com/cloudnineapps.com/wp-content/uploads/blogs/Technology/Code_Coverage_Is_Not_Enough/Code_Coverage_Delegate.png?resize=720%2C1005&amp;ssl=1 720w, https://i0.wp.com/cloudnineapps.com/wp-content/uploads/blogs/Technology/Code_Coverage_Is_Not_Enough/Code_Coverage_Delegate.png?resize=1080%2C1508&amp;ssl=1 1080w, https://i0.wp.com/cloudnineapps.com/wp-content/uploads/blogs/Technology/Code_Coverage_Is_Not_Enough/Code_Coverage_Delegate.png?resize=800%2C1117&amp;ssl=1 800w, https://i0.wp.com/cloudnineapps.com/wp-content/uploads/blogs/Technology/Code_Coverage_Is_Not_Enough/Code_Coverage_Delegate.png?resize=1024%2C1430&amp;ssl=1 1024w, https://i0.wp.com/cloudnineapps.com/wp-content/uploads/blogs/Technology/Code_Coverage_Is_Not_Enough/Code_Coverage_Delegate.png?resize=1280%2C1787&amp;ssl=1 1280w, https://i0.wp.com/cloudnineapps.com/wp-content/uploads/blogs/Technology/Code_Coverage_Is_Not_Enough/Code_Coverage_Delegate.png?resize=193%2C270&amp;ssl=1 193w, https://i0.wp.com/cloudnineapps.com/wp-content/uploads/blogs/Technology/Code_Coverage_Is_Not_Enough/Code_Coverage_Delegate.png?resize=300%2C419&amp;ssl=1 300w, https://i0.wp.com/cloudnineapps.com/wp-content/uploads/blogs/Technology/Code_Coverage_Is_Not_Enough/Code_Coverage_Delegate.png?w=1444&amp;ssl=1 1444w" sizes="(max-width: 640px) 100vw, 640px" data-recalc-dims="1" /></a></p>
<p><strong>Code Coverage of DAO getProduct() method<br />
</strong></p>
<p><a href="https://i2.wp.com/cloudnineapps.com/wp-content/uploads/blogs/Technology/Code_Coverage_Is_Not_Enough/Code_Coverage_DAO_Method.png?ssl=1"><img class="alignnone wp-image-3586 size-large" src="https://i2.wp.com/cloudnineapps.com/wp-content/uploads/blogs/Technology/Code_Coverage_Is_Not_Enough/Code_Coverage_DAO_Method.png?resize=640%2C490&#038;ssl=1" alt="" width="640" height="490" srcset="https://i2.wp.com/cloudnineapps.com/wp-content/uploads/blogs/Technology/Code_Coverage_Is_Not_Enough/Code_Coverage_DAO_Method.png?resize=1024%2C784&amp;ssl=1 1024w, https://i2.wp.com/cloudnineapps.com/wp-content/uploads/blogs/Technology/Code_Coverage_Is_Not_Enough/Code_Coverage_DAO_Method.png?resize=300%2C230&amp;ssl=1 300w, https://i2.wp.com/cloudnineapps.com/wp-content/uploads/blogs/Technology/Code_Coverage_Is_Not_Enough/Code_Coverage_DAO_Method.png?resize=768%2C588&amp;ssl=1 768w, https://i2.wp.com/cloudnineapps.com/wp-content/uploads/blogs/Technology/Code_Coverage_Is_Not_Enough/Code_Coverage_DAO_Method.png?resize=320%2C245&amp;ssl=1 320w, https://i2.wp.com/cloudnineapps.com/wp-content/uploads/blogs/Technology/Code_Coverage_Is_Not_Enough/Code_Coverage_DAO_Method.png?resize=640%2C490&amp;ssl=1 640w, https://i2.wp.com/cloudnineapps.com/wp-content/uploads/blogs/Technology/Code_Coverage_Is_Not_Enough/Code_Coverage_DAO_Method.png?resize=360%2C276&amp;ssl=1 360w, https://i2.wp.com/cloudnineapps.com/wp-content/uploads/blogs/Technology/Code_Coverage_Is_Not_Enough/Code_Coverage_DAO_Method.png?resize=720%2C551&amp;ssl=1 720w, https://i2.wp.com/cloudnineapps.com/wp-content/uploads/blogs/Technology/Code_Coverage_Is_Not_Enough/Code_Coverage_DAO_Method.png?resize=1080%2C827&amp;ssl=1 1080w, https://i2.wp.com/cloudnineapps.com/wp-content/uploads/blogs/Technology/Code_Coverage_Is_Not_Enough/Code_Coverage_DAO_Method.png?resize=800%2C612&amp;ssl=1 800w, https://i2.wp.com/cloudnineapps.com/wp-content/uploads/blogs/Technology/Code_Coverage_Is_Not_Enough/Code_Coverage_DAO_Method.png?resize=1280%2C980&amp;ssl=1 1280w, https://i2.wp.com/cloudnineapps.com/wp-content/uploads/blogs/Technology/Code_Coverage_Is_Not_Enough/Code_Coverage_DAO_Method.png?resize=353%2C270&amp;ssl=1 353w, https://i2.wp.com/cloudnineapps.com/wp-content/uploads/blogs/Technology/Code_Coverage_Is_Not_Enough/Code_Coverage_DAO_Method.png?w=1526&amp;ssl=1 1526w" sizes="(max-width: 640px) 100vw, 640px" data-recalc-dims="1" /></a></p>
<p>As we can see, the code coverage seems to be fairly good.</p>
<h2>Why Code Coverage Is Not Enough?</h2>
<p>Now, we can talk about why code coverage is not enough. The code exhibits 2 commonly observed issues in coding.</p>
<ol>
<li><strong>Missing Code For Functionality</strong>: The <span style="font-family: courier new, courier, monospace;">getProductPrice()</span> method does not have code to gracefully handle the scenario when the product is not found. Instead, it simply propagates the exception.</li>
<li><strong>Defective Code For Functionality</strong>: The <span style="font-family: courier new, courier, monospace;">getProductPriceChange()</span> method does not correctly handle the scenario when the product is not found. In this case, the price change will actually return the current price of the product thus leading to incorrect behavior. This is a defect.</li>
</ol>
<p>So, even though the code coverage is indicating a very high number (almost 100%), it by no means is a guarantee that the code is going to meet the business requirements. The first issue is very obvious as that code does not even exist in the production code. So, there is no way a code coverage tool will be able to measure its impact. However, the second issue is more subtle. It is an indication of how critical defects can easily slip through even when the code coverage was so high. Hence, it is extremely important that code coverage is not used solely to gauge the quality of code, especially by the developers.</p>
<p>Another important observation is the code coverage tool will report a line to be covered as long as it was hit at least once in the entire execution. However, as we can see, 2 of our methods have not been tested for missing product and hence the line where the DAO is generating the <span style="font-family: courier new, courier, monospace;">IllegalArgumentException</span> is not even hit in the unit testing for these methods. But, it was hit for the get product details unit tests and hence the code coverage tool will report it as covered. While some code coverage tools may be better than others, it is perhaps easy to understand for a developer that these type of details are often buried in details and may not be easy to find. The point being, just because a line is covered in the code coverage report, it is not an indication that it is covered for each relevant functional scenario.</p>
<h2>So, What&#8217;s The Solution?</h2>
<p>In my experience, a combination of good <strong>functional unit tests along with code coverage</strong> is a reasonably good way of tackling this. A functional unit test is written with the business requirement in mind (like the one we saw in our sample code). There are many advantages to using this approach.</p>
<ul>
<li>Firstly, you stop thinking in terms of making permutations and combinations, which are easy to miss. Many developers (including some of the smartest ones) are confused when it comes to unit testing. &#8220;What should I write? If a method takes 3 parameters, should I just pass null and combinations of these in my unit tests.&#8221; Not that this thought process is not important. But, functional unit tests make it easy to think in terms of how the product will be used and thus what positive and negative scenarios could arise.</li>
<li>The functional unit tests focus on real-world scenarios and make sure your code can handle these correctly. Hence, by writing these you are not only making the code more robust but also ensuring that you are not missing any critical functionality.</li>
<li>As a developer, your focus changes from attaining a code coverage number to ensuring as much of relevant functional scenarios are covered as possible.</li>
<li>I firmly believe that writing functional unit tests is a lot more fun. Sometimes, you have to be bit creative, both from coding the tests as well as coming up with scenarios.</li>
</ul>
<p>I am sure at this point someone may be thinking &#8220;What about QA folks? Won&#8217;t they cover it in their tests?&#8221; The answer is &#8220;It is quite likely they would.&#8221; But, as a developer, you do not want to rely on giving a drop to QA to measure its quality. If you could unit test your code more effectively, won&#8217;t the code be much better and make you a stronger professional? Besides, the later the defects are found, the more expensive are these to fix. So, the sooner, you can take care of issues in the development lifecycle, the better. And, when you combine these functional unit tests with a continuously improving code coverage, you have a lot more reliable and robust code that meets the business needs.</p>
<p>Happy coding!<br />
&#8211; Nitin</p>
<p>&nbsp;</p>
<p><div class="su-note"  style="border-color:#e5d6d4;border-radius:5px;-moz-border-radius:5px;-webkit-border-radius:5px;"><div class="su-note-inner su-u-clearfix su-u-trim" style="background-color:#fff0ee;border-color:#ffffff;color:#333333;border-radius:5px;-moz-border-radius:5px;-webkit-border-radius:5px;"></p>
<center>
<h4>Enhance your AWS skills with these hands-on courses for real-world deployments.</h4>
</center>
<p><div class="su-row"> <div class="su-column su-column-size-1-2"><div class="su-column-inner su-u-clearfix su-u-trim"></p>
<p><a href="https://cloudnineapps.com/courses/cloud-computing/cloud-computing-amazon-web-services-aws-overview/"><img class="size-full wp-image-2069 aligncenter" src="https://i0.wp.com/cloudnineapps.com/wp-content/uploads/courses/Cloud_Computing/AWS_Overview/AWS_Overview_Logo_small.png?resize=192%2C108&#038;ssl=1" alt="" width="192" height="108" data-recalc-dims="1" /></a></p>
<center>Learn AWS basics for FREE.</center>
<p></div></div> <div class="su-column su-column-size-1-2"><div class="su-column-inner su-u-clearfix su-u-trim"></p>
<p><a href="https://cloudnineapps.com/courses/cloud-computing/aws-advanced-for-developers/"><img class="size-full wp-image-2068 aligncenter" src="https://i0.wp.com/cloudnineapps.com/wp-content/uploads/courses/Cloud_Computing/AWS_Advanced_For_Developers/AWS_Advanced_For_Developers_Logo_small.png?resize=192%2C108&#038;ssl=1" alt="" width="192" height="108" data-recalc-dims="1" /></a></p>
<center>Learn practical application development on AWS.</center>
<p></div></div> </div></div></div></p>
<p><a class="a2a_button_facebook" href="https://www.addtoany.com/add_to/facebook?linkurl=https%3A%2F%2Fcloudnineapps.com%2Fblogs%2Ftechnology%2Fcode-coverage-is-not-enough%2F&amp;linkname=Code%20Coverage%20Is%20Not%20Enough" title="Facebook" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_twitter" href="https://www.addtoany.com/add_to/twitter?linkurl=https%3A%2F%2Fcloudnineapps.com%2Fblogs%2Ftechnology%2Fcode-coverage-is-not-enough%2F&amp;linkname=Code%20Coverage%20Is%20Not%20Enough" title="Twitter" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_linkedin" href="https://www.addtoany.com/add_to/linkedin?linkurl=https%3A%2F%2Fcloudnineapps.com%2Fblogs%2Ftechnology%2Fcode-coverage-is-not-enough%2F&amp;linkname=Code%20Coverage%20Is%20Not%20Enough" title="LinkedIn" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_pinterest" href="https://www.addtoany.com/add_to/pinterest?linkurl=https%3A%2F%2Fcloudnineapps.com%2Fblogs%2Ftechnology%2Fcode-coverage-is-not-enough%2F&amp;linkname=Code%20Coverage%20Is%20Not%20Enough" title="Pinterest" rel="nofollow noopener" target="_blank"></a><a class="a2a_dd a2a_counter addtoany_share_save addtoany_share" href="https://www.addtoany.com/share#url=https%3A%2F%2Fcloudnineapps.com%2Fblogs%2Ftechnology%2Fcode-coverage-is-not-enough%2F&#038;title=Code%20Coverage%20Is%20Not%20Enough" data-a2a-url="https://cloudnineapps.com/blogs/technology/code-coverage-is-not-enough/" data-a2a-title="Code Coverage Is Not Enough"></a></p><p>The post <a rel="nofollow" href="https://cloudnineapps.com/blogs/technology/code-coverage-is-not-enough/">Code Coverage Is Not Enough</a> appeared first on <a rel="nofollow" href="https://cloudnineapps.com">Cloud Nine Apps</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://cloudnineapps.com/blogs/technology/code-coverage-is-not-enough/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">3580</post-id>	</item>
		<item>
		<title>Be A Smart Online Shopper</title>
		<link>https://cloudnineapps.com/blogs/technology/be-a-smart-online-shopper/</link>
					<comments>https://cloudnineapps.com/blogs/technology/be-a-smart-online-shopper/#respond</comments>
		
		<dc:creator><![CDATA[Nitin Patil]]></dc:creator>
		<pubDate>Tue, 28 May 2019 18:51:24 +0000</pubDate>
				<category><![CDATA[Technology]]></category>
		<guid isPermaLink="false">https://cloudnineapps.com/?p=3513</guid>

					<description><![CDATA[<p>Be A Smart Online Shopper Online shopping has been popular for many years now. It is fast and convenient with lots of choices. On top of that leading online retailers have been trying to make it as convenient as possible&#8230;&#160;<a href="https://cloudnineapps.com/blogs/technology/be-a-smart-online-shopper/" class="more-link">Read More</a></p>
<p>The post <a rel="nofollow" href="https://cloudnineapps.com/blogs/technology/be-a-smart-online-shopper/">Be A Smart Online Shopper</a> appeared first on <a rel="nofollow" href="https://cloudnineapps.com">Cloud Nine Apps</a>.</p>
]]></description>
										<content:encoded><![CDATA[<h1>Be A Smart Online Shopper</h1>
<p>Online shopping has been popular for many years now. It is fast and convenient with lots of choices. On top of that leading online retailers have been trying to make it as convenient as possible to deliver everything from daily necessities to luxury goods. And, it would be fair to say in this digital age many of us have resorted to using online shopping as the de facto mode of shopping for most needs if not all. Having said that, online shoppers can benefit from applying some simple tweaks to the way they shop. And, if you are already doing these, well, you are already smart then!</p>
<h2>Buying What You Need When You Need</h2>
<p>Before we touch upon how to be a smart online shopper, let&#8217;s just understand a fairly common online shopping behavior. You wake up one day and realize you need to buy an item or perhaps a bunch of items. It could be the birthday gift for someone special, an item that you needed, but did not have time to research and it&#8217;s needed now, or just a situation that the holidays are about to start and you have not gotten any gifts for your loved ones. I am sure many of us can relate to such situations without thinking hard. And, there is nothing wrong about it. You have a need and you rush to your favorite online shopping site(s), perhaps look for some price comparisons, shipping costs, etc and make your best possible purchase. Sometimes you may not even have time for that. After all, the routine takes over and you have to make some hasty decisions. With online shopping, this behavior is easier to get into as you do not have to go anywhere. You know all you need to do is just go online and place an order. So, things could get procrastinated easily. Although, not always intentionally.</p>
<h2>Cut To Smart Shopping</h2>
<p>Smart shopping is also about what you need. But, not necessarily doing it <span style="text-decoration: underline;">when</span> you need it. IMHO smart shopping involves the following.</p>
<ul>
<li><strong>Plan for what you need a bit ahead of time</strong>: While you may not be able to predict every single need, chances are high that you can predict several of your upcoming needs, at least, for the near future. Think of things like birthdays, anniversaries, festivals, those home improvements you want to do, things that can improve your lifestyle, health, and so on. There are many advantages to planning a bit ahead.
<ul>
<li>You can plan your budget better. For example, instead of having all the gifts bought in the same month, maybe you could spread the purchase costs over a couple of months to reduce your credit card bill.</li>
<li>I believe an important aspect of shopping gifts is buying things that the recipients will really like and not just something that you want to give. By planning ahead, you are giving yourself some lead time to find the most appropriate gifts that they would like. Again, you do not have to do all the research at once. Spread it over time so that you can do a good job while not impacting your other commitments.</li>
</ul>
</li>
<li><strong>Look for the best deals</strong>: Who does not love deals? Online shopping offers many venues to save money from coupons to limited time deals. The challenge is how to find good deals? There are some well-known practices followed by many smart shoppers.
<ul>
<li>Use price comparison sites like <a href="http://www.pricegrabber.com/" rel="noopener">pricegrabber.com</a>, <a href="http://www.bizrate.com/" rel="noopener">bizrate.com</a>, etc.</li>
<li>For coupons, use sites like <a href="https://www.retailmenot.com/" rel="noopener">retailmenot.com</a>, <a href="https://www.groupon.com/" rel="noopener">groupon.com</a>, and so on.</li>
<li>Prices of several items vary over the year. This is due to the obvious nature of <strong>supply-and-demand</strong>. For example, a summer item is likely to be somewhat cheaper when bought in winter because of the low demand. So, if you could plan your purchases such that you make these during the low-demand period, you may be able to save yourself some money.</li>
</ul>
</li>
<li><strong>Buy when the price drops</strong>: The key difference between this and the earlier point of looking for best deals is in this case you buy only when the price drops. The advantage is instead of you spending time finding coupons and comparing prices, you simply get notified when the price drops and then you make your purchase saving yourself both time and money. There are sites like <a href="https://camelcamelcamel.com/" rel="noopener">camelcamelcamel.com</a> that can track prices and notify you when the price drops.</li>
</ul>
<p>Often, a combination of these strategies can help achieve a more satisfying shopping experience in terms of time, savings and the value you get of your purchases.</p>
<h2>Taking Smart Shopping One Step Further</h2>
<p>You may be wondering what more can be done? Being a DIY (do-it-yourself) homeowner and an online shopper, I firmly believe that the value of shopping is not just in getting the best deal, but in getting the <strong>best value out of a purchase</strong>. Hence, I like to think of purchases as projects. This makes my purchases goal-driven and the value realization is often much better than if I were to buy the items individually. What could be a project? Say, you want to set up a home theater, you could treat it like a project and organize the items you want to buy as part of that project. Over time, I&#8217;ve seen the following benefits of this project-based approach.</p>
<ul>
<li>Firstly, you can have a better overall estimate of how much you will need to spend, which helps in budgeting and identifying how these purchases will be funded.</li>
<li>You do not have to buy everything at once. Buy when the prices drop or at a point that is more comfortable for you.</li>
<li>You can track which items have already been bought and which are remaining. That way, you can plan for the pending item purchases.</li>
<li>Last, but not the least, since these purchases are with a goal in mind, the sense of accomplishment is higher than buying the items individually.</li>
</ul>
<div class="alignright"><iframe width="560" height="315" src="https://www.youtube.com/embed/2SgEKdPwZrA?rel=0" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen="allowfullscreen"></iframe></p>
    <style type="text/css">
        .dark_theme{
            padding: 8px; 
            background: rgb(85, 85, 85);
        }
    </style>
    <div class="ytsubscribe_container default_theme">
    	<script src="https://apis.google.com/js/platform.js"></script>
    	    	<div 
    		class="g-ytsubscribe" 
    		data-channelid="UC3xhAVjMLR_77qT5I_8i9Fg" 
    		data-layout="default" 
    		data-theme="default" 
    		data-count="hidden">			
    	</div>
    </div>
	
</div>
<p>If it sounds too much, it is not in reality, especially given the value. You just need a way to ensure you can manage purchases like a project. And, this is precisely one of the main reasons why I came up with the idea of the FREE <a href="https://cloudnineapps.com/products/mobile/shopping-guru/" rel="noopener">Shopping Guru</a> app, which helps in planning, saving and buying with Amazon. In fact, an important point for many online shoppers is what all should you buy for a given goal? If it is something that you are well-versed with, that&#8217;s good. But, that may not be the case always. So, how do you get a jump start? Again, this is where the Shopping Guru app takes a novel approach of providing out-of-the-box <strong>project templates</strong> that can give you a starting point for what all you should buy for some common goals. Of course, the actual items bought can differ from person to person based on price point, aesthetics, etc. But, the template gives you a reasonable starting point. And, if you do not find a template that matches your needs, you can start from the &#8220;Blank Project&#8221; template. You will still get the benefits of organizing the items under a project, overall budget estimate, manage which purchases have been completed, and so on. On top of this, the app also provides managing ad hoc item purchases for those one-off needs, <strong>price watch</strong>, and <strong>price drop alert</strong> along with a convenient <strong>Savings Dashboard</strong> to track your savings over time.</p>
<p><em>“As a DIY homeowner I have done several projects over </em>the years<em> and I do the majority of my shopping on Amazon because of the number of choices and their fast delivery. However, the challenge was I just did not know what all I should buy and I wished there was something that gave me a jump start. Over the years as I helped other members in the community, I sensed this is a common problem and that’s when I thought to have an app that provides a template-based approach will be helpful to many people. The template can guide on what all you should look for. Of course, the actual items bought may be different from person to person based on prices, aesthetics and other criteria. But, at least it will give a good starting point. Secondly, because Amazon’s site has frequent price changes, it would be nice if there were a way to track price drops and save money. I put these ideas together and Shopping Guru was born.&#8221;</em></p>
<p>The app does a best effort to make the most out of your purchases. However, you can use any other means of your choice. Interestingly, at times I like to take savings from my earlier projects to fund some future projects, which in turn may also save some money. And, the shopping journey continues!</p>
<p>Note that the project-based approach does not completely eliminate the need for ad hoc purchases. For example, those batteries that you always need around the home and other daily necessities. But, it can certainly be helpful in managing other purchases where you are trying to achieve some target. I like to call it <strong>value-driven shopping</strong>, which helps in achieving goals small or big and over time these values combined result in much higher satisfaction from the online shopping.</p>
<p>Happy smart shopping!<br />
&#8211; Nitin</p>
<p><div class="su-note"  style="border-color:#e5d6d4;border-radius:5px;-moz-border-radius:5px;-webkit-border-radius:5px;"><div class="su-note-inner su-u-clearfix su-u-trim" style="background-color:#fff0ee;border-color:#ffffff;color:#333333;border-radius:5px;-moz-border-radius:5px;-webkit-border-radius:5px;"><center><strong>Interested in making the most out of your Amazon purchases?</strong></center><a href="https://cloudnineapps.com/products/mobile/shopping-guru/"><img class="alignleft wp-image-2666 size-thumbnail" src="https://i2.wp.com/cloudnineapps.com/wp-content/uploads/products/mobile/ShoppingGuru/ShoppingGuru_Logo_Circle.png?resize=150%2C150&#038;ssl=1" alt="Shopping Guru - Plan, Shop &amp; Save With Amazon" width="150" height="150" data-recalc-dims="1" /></a></p>
<ul>
	<li>Make goal driven purchases</li>
	<li>Manage ad hoc item purchases</li>
	<li>Monitor prices and get price drop alerts</li>
	<li>Buy when prices drop and save money!</li>
</ul>
<p>Use the FREE <a href="https://cloudnineapps.com/products/mobile/shopping-guru/">Shopping Guru</a> mobile app to plan, shop and save with Amazon.</p>
<p></div></div></p>
<p>&nbsp;</p>
<p><a class="a2a_button_facebook" href="https://www.addtoany.com/add_to/facebook?linkurl=https%3A%2F%2Fcloudnineapps.com%2Fblogs%2Ftechnology%2Fbe-a-smart-online-shopper%2F&amp;linkname=Be%20A%20Smart%20Online%20Shopper" title="Facebook" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_twitter" href="https://www.addtoany.com/add_to/twitter?linkurl=https%3A%2F%2Fcloudnineapps.com%2Fblogs%2Ftechnology%2Fbe-a-smart-online-shopper%2F&amp;linkname=Be%20A%20Smart%20Online%20Shopper" title="Twitter" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_linkedin" href="https://www.addtoany.com/add_to/linkedin?linkurl=https%3A%2F%2Fcloudnineapps.com%2Fblogs%2Ftechnology%2Fbe-a-smart-online-shopper%2F&amp;linkname=Be%20A%20Smart%20Online%20Shopper" title="LinkedIn" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_pinterest" href="https://www.addtoany.com/add_to/pinterest?linkurl=https%3A%2F%2Fcloudnineapps.com%2Fblogs%2Ftechnology%2Fbe-a-smart-online-shopper%2F&amp;linkname=Be%20A%20Smart%20Online%20Shopper" title="Pinterest" rel="nofollow noopener" target="_blank"></a><a class="a2a_dd a2a_counter addtoany_share_save addtoany_share" href="https://www.addtoany.com/share#url=https%3A%2F%2Fcloudnineapps.com%2Fblogs%2Ftechnology%2Fbe-a-smart-online-shopper%2F&#038;title=Be%20A%20Smart%20Online%20Shopper" data-a2a-url="https://cloudnineapps.com/blogs/technology/be-a-smart-online-shopper/" data-a2a-title="Be A Smart Online Shopper"></a></p><p>The post <a rel="nofollow" href="https://cloudnineapps.com/blogs/technology/be-a-smart-online-shopper/">Be A Smart Online Shopper</a> appeared first on <a rel="nofollow" href="https://cloudnineapps.com">Cloud Nine Apps</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://cloudnineapps.com/blogs/technology/be-a-smart-online-shopper/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">3513</post-id>	</item>
	</channel>
</rss>
