{"id":2316,"date":"2018-10-23T13:33:59","date_gmt":"2018-10-23T13:33:59","guid":{"rendered":"https:\/\/ionicframework.com\/?p=2316"},"modified":"2018-12-12T22:39:22","modified_gmt":"2018-12-12T22:39:22","slug":"built-with-ionic-juntoscope","status":"publish","type":"post","link":"https:\/\/ionic.io\/blog\/built-with-ionic-juntoscope","title":{"rendered":"Built with Ionic: JuntoScope"},"content":{"rendered":"<p style=\"text-align: left;\"><em>This is a guest post by Jedi Weller and his team at <a href=\"http:\/\/www.openforge.io\/\" target=\"_blank\" rel=\"noopener\">OpenForge<\/a>. They have been pioneering new ways of designing digital solutions, integrating cross-disciplinary teams, and sharing common knowledge across technology companies in various verticals. Their work at OpenForge continues to lay the groundwork for clearer communication and enhanced transparency in the technology and startup communities.<\/em><\/p>\n<h1>Dev Scoping &#8211; The Final Frontier<\/h1>\n<p><a href=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/10\/JuntoScope-header.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"200\" class=\"alignnone size-full wp-image-2323 lazyload\" data-src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/10\/JuntoScope-header.jpg\" alt=\"\" data-srcset=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/10\/JuntoScope-header.jpg 800w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/10\/JuntoScope-header-300x75.jpg 300w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/10\/JuntoScope-header-768x192.jpg 768w\" data-sizes=\"auto, (max-width: 800px) 100vw, 800px\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" style=\"--smush-placeholder-width: 800px; --smush-placeholder-aspect-ratio: 800\/200;\" \/><noscript><img loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"200\" class=\"alignnone size-full wp-image-2323\" src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/10\/JuntoScope-header.jpg\" alt=\"\" srcset=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/10\/JuntoScope-header.jpg 800w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/10\/JuntoScope-header-300x75.jpg 300w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/10\/JuntoScope-header-768x192.jpg 768w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><\/noscript><\/a><br \/>\nIn our industry, it\u2019s often the case that software developers feel frustrated with the scoping process and the \u2018need\u2019 to put a \u2018fixed\u2019 number of hours on a complex feature. Developers everywhere try to estimate based on experience and past projects, but the number of variables in software development scoping can prove to be a perilous task.<\/p>\n<p>This problem is exacerbated in team environments with Junior and Senior developers working together toward common solutions. This often results in Junior devs feeling \u2018peer pressured\u2019 (even unintentionally) into reducing their scopes to match other developers.<\/p>\n<p>So, we built a way to help.<\/p>\n<p><a href=\"http:\/\/try.juntoscope.com\/\">JuntoScope<\/a> (\u201cTogether We Scope\u201d) is a <strong>free, open-sourced, anonymous scoping tool for teams. We built it using Ionic V3, Firebase 4.12.1, a Serverless (FaaS) Architecture, and the Teamwork\u2019s Projects API. The app is available on <a href=\"https:\/\/play.google.com\/store\/apps\/details?id=com.openforge.juntoscope\">Google Play<\/a> and the <a href=\"https:\/\/itunes.apple.com\/us\/app\/juntoscope\/id1421846154?ls=1&amp;mt=8\">App Store<\/a>.<\/strong><\/p>\n<p><!--more--><\/p>\n<p>So &#8211; let\u2019s talk about our experience in building JuntoScope. No marketing, no bs, just straight up \u201chere\u2019s how it works.\u201d After all, it\u2019s a free tool &#8211; we don\u2019t care if you use it or steal it, but at least buy us a beer the next time you see us \ud83d\ude42<\/p>\n<h2>Designing The App<\/h2>\n<p><a href=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/10\/UI-Scoping-as-a-Moderator.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"452\" class=\"alignnone size-large wp-image-2330 lazyload\" data-src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/10\/UI-Scoping-as-a-Moderator-1024x452.jpg\" alt=\"\" data-srcset=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/10\/UI-Scoping-as-a-Moderator-1024x452.jpg 1024w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/10\/UI-Scoping-as-a-Moderator-300x132.jpg 300w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/10\/UI-Scoping-as-a-Moderator-768x339.jpg 768w\" data-sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" style=\"--smush-placeholder-width: 1024px; --smush-placeholder-aspect-ratio: 1024\/452;\" \/><noscript><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"452\" class=\"alignnone size-large wp-image-2330\" src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/10\/UI-Scoping-as-a-Moderator-1024x452.jpg\" alt=\"\" srcset=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/10\/UI-Scoping-as-a-Moderator-1024x452.jpg 1024w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/10\/UI-Scoping-as-a-Moderator-300x132.jpg 300w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/10\/UI-Scoping-as-a-Moderator-768x339.jpg 768w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/noscript><\/a><br \/>\nThe JuntoScope design process was actually <strong>led<\/strong> by our development team, which was a fun change of pace. It was the dev team\u2019s first time acting as the client, so we used a couple of our team\u2019s processes to facilitate the communication between design &amp; feature requirements.<\/p>\n<ol>\n<li>We started by printing these <a href=\"https:\/\/www.pixeden.com\/psd-mock-up-templates\/iphone-6-psd-vector-mockup\" target=\"_blank\" rel=\"noopener\">iPhone printouts<\/a> and then hand-drawing the flow.<\/li>\n<li>We migrated these to <a href=\"https:\/\/www.sketchapp.com\/\" target=\"_blank\" rel=\"noopener\">Sketch<\/a>, with the design team using <a href=\"https:\/\/www.goabstract.com\/\" target=\"_blank\" rel=\"noopener\">Abstract<\/a> for design source control. We used the <a href=\"https:\/\/www.producthunt.com\/posts\/ionic-sketch-ui-kit\" target=\"_blank\" rel=\"noopener\">Sketch Ionic UI Plugin<\/a> to make design easy.<\/li>\n<li>In order to keep language the same for designers and developers, we created a \u2018legend\u2019, that represents page transitions, routing, and data passthrough, and then started building!<\/li>\n<\/ol>\n<p style=\"text-align: center;\"><a href=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/10\/Screen-Shot-2018-10-16-at-9.52.41-AM.png\"><img loading=\"lazy\" decoding=\"async\" width=\"382\" height=\"120\" class=\"aligncenter size-full wp-image-2343 lazyload\" data-src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/10\/Screen-Shot-2018-10-16-at-9.52.41-AM.png\" alt=\"\" data-srcset=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/10\/Screen-Shot-2018-10-16-at-9.52.41-AM.png 382w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/10\/Screen-Shot-2018-10-16-at-9.52.41-AM-300x94.png 300w\" data-sizes=\"auto, (max-width: 382px) 100vw, 382px\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" style=\"--smush-placeholder-width: 382px; --smush-placeholder-aspect-ratio: 382\/120;\" \/><noscript><img loading=\"lazy\" decoding=\"async\" width=\"382\" height=\"120\" class=\"aligncenter size-full wp-image-2343\" src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/10\/Screen-Shot-2018-10-16-at-9.52.41-AM.png\" alt=\"\" srcset=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/10\/Screen-Shot-2018-10-16-at-9.52.41-AM.png 382w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/10\/Screen-Shot-2018-10-16-at-9.52.41-AM-300x94.png 300w\" sizes=\"auto, (max-width: 382px) 100vw, 382px\" \/><\/noscript><\/a><\/p>\n<p>We open-sourced the sketch file, which can be found <a href=\"https:\/\/openforge.io\/assets\/pdf\/juntoscope.sketch\">here<\/a>.<\/p>\n<h2>Folder Structure<\/h2>\n<p>We opted for a feature-oriented file structure to focus on the <strong>four<\/strong> distinct features in JuntoScope. We believe this will scale nicely as we build further support and features.<\/p>\n<p style=\"float: right;\"><em><a href=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/10\/Screen-Shot-2018-10-09-at-10.47.30-AM-1.png\"><img loading=\"lazy\" decoding=\"async\" width=\"300\" height=\"734\" class=\"size-full wp-image-2339 alignright lazyload\" data-src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/10\/Screen-Shot-2018-10-09-at-10.47.30-AM-1.png\" alt=\"\" data-srcset=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/10\/Screen-Shot-2018-10-09-at-10.47.30-AM-1.png 300w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/10\/Screen-Shot-2018-10-09-at-10.47.30-AM-1-123x300.png 123w\" data-sizes=\"auto, (max-width: 300px) 100vw, 300px\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" style=\"--smush-placeholder-width: 300px; --smush-placeholder-aspect-ratio: 300\/734;\" \/><noscript><img loading=\"lazy\" decoding=\"async\" width=\"300\" height=\"734\" class=\"size-full wp-image-2339 alignright\" src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/10\/Screen-Shot-2018-10-09-at-10.47.30-AM-1.png\" alt=\"\" srcset=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/10\/Screen-Shot-2018-10-09-at-10.47.30-AM-1.png 300w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/10\/Screen-Shot-2018-10-09-at-10.47.30-AM-1-123x300.png 123w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/noscript><\/a><\/em><\/p>\n<ul>\n<li style=\"text-align: left;\">App&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\n<ul>\n<li>Generic app bootstrapping, auth guard, etc.<\/li>\n<\/ul>\n<\/li>\n<li>Assets\n<ul>\n<li>This project is simple, so we just keep the @fonts\/folder in assets. In larger projects, we keep shared file assets and an organized file directory.<\/li>\n<\/ul>\n<\/li>\n<li>Environments\n<ul>\n<li>Stores our production vs. dev environment configuration<\/li>\n<\/ul>\n<\/li>\n<li>Features\n<ul>\n<li>We opted for a feature-oriented file structure, with four main features<\/li>\n<\/ul>\n<\/li>\n<li>Models\n<ul>\n<li>Define our data models across the app<\/li>\n<\/ul>\n<\/li>\n<li>Shared\n<ul>\n<li>We always include pop-ups, modals, etc. in a \u201cshared\u201d folder in our projects<\/li>\n<\/ul>\n<\/li>\n<li>Store\n<ul>\n<li><a href=\"https:\/\/github.com\/ngrx\/store\" target=\"_blank\" rel=\"noopener\">See NGRX<\/a><\/li>\n<\/ul>\n<\/li>\n<li>Theme\n<ul>\n<li>Shared variables.scss<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<h2>Commit Sanity<\/h2>\n<p>We can\u2019t emphasize enough the importance of succinct, organized commits. Specifically, we used these tools:<\/p>\n<p><a href=\"https:\/\/github.com\/commitizen\" target=\"_blank\" rel=\"noopener\">Commitizen<\/a> &#8211; it\u2019s freaking amazing (but also frustrating when you first use it).<br \/>\n<a href=\"https:\/\/github.com\/palantir\/tslint\" target=\"_blank\" rel=\"noopener\">Tslint<\/a> &#8211; Yes, absolutely necessary.<br \/>\n<a href=\"https:\/\/github.com\/prettier\/prettier\" target=\"_blank\" rel=\"noopener\">Prettier<\/a> &#8211; Seriously, do you want to worry about line-wrapping?<\/p>\n<h2>An Intern\u2019s Perspective &#8211; Serverless Architecture<\/h2>\n<p><a href=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/10\/headshot-claudio.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"300\" height=\"300\" class=\"alignleft size-full wp-image-2331 lazyload\" data-src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/10\/headshot-claudio.jpg\" alt=\"\" data-srcset=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/10\/headshot-claudio.jpg 300w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/10\/headshot-claudio-150x150.jpg 150w\" data-sizes=\"auto, (max-width: 300px) 100vw, 300px\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" style=\"--smush-placeholder-width: 300px; --smush-placeholder-aspect-ratio: 300\/300;\" \/><noscript><img loading=\"lazy\" decoding=\"async\" width=\"300\" height=\"300\" class=\"alignleft size-full wp-image-2331\" src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/10\/headshot-claudio.jpg\" alt=\"\" srcset=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/10\/headshot-claudio.jpg 300w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/10\/headshot-claudio-150x150.jpg 150w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/noscript><\/a><br \/>\n<em>We used this project as a great learning experience for one of our long-term interns, Claudio Del Valle, who\u2019s been with us twice now during his term at Drexel. So, we felt it only appropriate he should get a chance to explain the architecture. <a href=\"https:\/\/github.com\/openforge\" target=\"_blank\" rel=\"noopener\">Check out the code for more details<\/a>.<\/em><\/p>\n<p><em>We wanted to provide a seamless experience across all platforms, so we were looking for responsiveness and data persistence. In this case, using REST would create too much overhead and would require an overly complicated architecture. Therefore, we needed a simple way to establish a real-time connection between the moderator, participants, and our backend. I imagine some readers are already thinking WebSockets by now. We thought about it too, but we turned to Firebase instead.<\/em><\/p>\n<p><em>As I alluded to above, WebSockets have been the popular choice for many developers who are looking for real-time, bi-directional communication between client and server. They provide excellent performance and security. Additionally, using a WebSocket-based implementation would be cheaper than using what&#8217;s sometimes called a &#8216;pull&#8217; system. This is when the client-side has to ask the server for new information constantly. Instead, using WebSockets can be thought of as a &#8216;push&#8217; system, where the server can emit values to the clients and vice versa.<\/em><\/p>\n<p><em>Our project architecture could be described as serverless. This means that we use a service that manages the servers for us, namely, Firebase. It not only hosts our data, but our backend consists of an Express app hosted as Cloud Functions on Firebase. Now, had we decided to use WebSockets and wanted to remain serverless, we most likely would&#8217;ve had to use a different service to host our server where we would manage the sockets manually.<\/em><br \/>\n<em>Here is where we decided to remain as lean as possible by leveraging the service we were already using. Furthermore, by using a popular Angular library for Firebase, <a href=\"https:\/\/github.com\/angular\/angularfire2\" target=\"_blank\" rel=\"noopener\">angularfire2<\/a>, we were able to get the result we wanted.\u201d<\/em><\/p>\n<h2>Custom Styling<\/h2>\n<p style=\"text-align: left;\"><em><a style=\"float: left; max-width: 300px;\" href=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/10\/Screen-Shot-2018-10-09-at-10.48.08-AM.png\"><img loading=\"lazy\" decoding=\"async\" width=\"563\" height=\"1024\" class=\"size-large wp-image-2338 alignnone lazyload\" data-src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/10\/Screen-Shot-2018-10-09-at-10.48.08-AM-563x1024.png\" alt=\"\" data-srcset=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/10\/Screen-Shot-2018-10-09-at-10.48.08-AM-563x1024.png 563w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/10\/Screen-Shot-2018-10-09-at-10.48.08-AM-165x300.png 165w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/10\/Screen-Shot-2018-10-09-at-10.48.08-AM.png 576w\" data-sizes=\"auto, (max-width: 563px) 100vw, 563px\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" style=\"--smush-placeholder-width: 563px; --smush-placeholder-aspect-ratio: 563\/1024;\" \/><noscript><img loading=\"lazy\" decoding=\"async\" width=\"563\" height=\"1024\" class=\"size-large wp-image-2338 alignnone\" src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/10\/Screen-Shot-2018-10-09-at-10.48.08-AM-563x1024.png\" alt=\"\" srcset=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/10\/Screen-Shot-2018-10-09-at-10.48.08-AM-563x1024.png 563w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/10\/Screen-Shot-2018-10-09-at-10.48.08-AM-165x300.png 165w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/10\/Screen-Shot-2018-10-09-at-10.48.08-AM.png 576w\" sizes=\"auto, (max-width: 563px) 100vw, 563px\" \/><\/noscript><\/a><\/em>Having an organized CSS and a style guide is <em>incredibly<\/em> important when creating an app. We always try to identify reusable and similar components to avoid code duplication and design inconsistency later down the line.<\/p>\n<p>How to organize the code is a matter of preference; Ionic recommends organizing by platform. For this project, we took that one step further and kept the order of variables consistent.<\/p>\n<p>For instance, our <code>&lt;ion-searchbar&gt;<\/code> component overrides are declared first in all 3 of our platform (iOS, Android, and Windows) sections.<\/p>\n<p>We\u2019ve also separated the responsibilities of variables.scss (framework variables) from app.scss (custom component styles) &#8211; though both are still global.<\/p>\n<h2>Lessons Learned &#8211; A Note From Paulina (<a href=\"https:\/\/twitter.com\/paulpaultweets\" target=\"_blank\" rel=\"noopener\">@paulpaultweets<\/a>)<\/h2>\n<p><a href=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/10\/headshot-paulina.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"300\" height=\"300\" class=\"size-full wp-image-2332 alignleft lazyload\" data-src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/10\/headshot-paulina.jpg\" alt=\"\" data-srcset=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/10\/headshot-paulina.jpg 300w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/10\/headshot-paulina-150x150.jpg 150w\" data-sizes=\"auto, (max-width: 300px) 100vw, 300px\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" style=\"--smush-placeholder-width: 300px; --smush-placeholder-aspect-ratio: 300\/300;\" \/><noscript><img loading=\"lazy\" decoding=\"async\" width=\"300\" height=\"300\" class=\"size-full wp-image-2332 alignleft\" src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/10\/headshot-paulina.jpg\" alt=\"\" srcset=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/10\/headshot-paulina.jpg 300w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/10\/headshot-paulina-150x150.jpg 150w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/noscript><\/a><em>You will notice at the bottom of variables.scss we <a href=\"https:\/\/github.com\/openforge\/JuntoScope\/blob\/ae38dc582bd763a4e57146b8b055bb79200f1ed7\/src\/theme\/variables.scss#L156-L218\" target=\"_blank\" rel=\"noopener\">import and declare our custom font<\/a> called \u201cAvenir Next\u201d. It\u2019s a lot of code for not a lot of value; so my recommendation is to avoid custom fonts, unless there is a quantifiable business justification for doing so. In addition, web application fonts result in very heavy load times. Keep this in mind!<\/em><\/p>\n<p><em>As we\u2019re building an application, I often find pieces I want to refactor and shrink down as I go along. I\u2019ve found it\u2019s best to backlog these items as I\u2019m working, so that I can focus on the task at hand but still make sure the refactoring is completed.<\/em><\/p>\n<h2>Bugs &amp; Challenges<\/h2>\n<p>The overall development and design of our Ionic V3 App took approximately 383 hours to build; we used <a href=\"https:\/\/www.teamwork.com\/partner\/gqfq4mb55o\" target=\"_blank\" rel=\"noopener\">Teamwork Projects<\/a> to track billing\/hours logging. Even though the scope of the project was not large (2-3 months), we had a couple of major setbacks along the way.<\/p>\n<ul>\n<li><strong>New Experience &#8211; Designing Error States:<\/strong> Since this was the first time our dev team led the design process, we missed some of our normal process that includes thinking through error states and transitions. Luckily, we were able to identify a couple holes we found in the flow through trial and error.<\/li>\n<li><strong>Change in Teamwork Authentication API:<\/strong> We found out on the app store submission date that Teamwork updated their API to a more oAuth based approach. Maybe we should have subscribed to those monthly newsletters earlier \ud83d\ude00<\/li>\n<li><strong>We had no marketing language:<\/strong> As previously mentioned, we never intended to market this tool &#8211; it was just something we built internally. So when all of a sudden we have questions relating to the privacy policy, TOS, or app store listing, everyone drew blanks and we had to scramble to get all these resources together.<\/li>\n<\/ul>\n<h2>Final Thoughts<\/h2>\n<p>Overall, the project was a lot of fun, and the front-end implementation, bolstered by Ionic framework v3, was incredibly straightforward to use. We were able to experiment with the Function as a Service architecture through Firebase, which was a lot of fun too.<\/p>\n<p>Most importantly, this tool allows us to really speed up our internal scoping process and keep our software development scoping ACCURATE by using the team average. The team likes this process, so we\u2019re going to keep using it.<\/p>\n<p>We\u2019d encourage anyone else using Teamwork Projects to try it out as well and let us know your thoughts. If enough people request, we have talked about adding support to tools like JIRA and Basecamp, but for now, we\u2019re just supporting one. By the way, the team over at <a href=\"https:\/\/www.teamwork.com\/\" target=\"_blank\" rel=\"noopener\">Teamwork.com<\/a> loves what we&#8217;ve built and has agreed to give a 10% discount to the community. Just use discount code \u2018JuntoScope\u2019. Thanks for the love Teamwork &lt;3.<\/p>\n<p>This project went <strong>blazingly fast<\/strong> by utilizing the Ionic Framework. The entire application was rebuilt from our proof of concept using Ionic 3 in just a couple of weeks, and the performance is fantastic. We schedule <strong>0 hours<\/strong> to work on performance optimizations, which is a blessing for anyone who knows how arduous performance optimization can be.<\/p>\n<p>If you DO decide to use it, we\u2019ll be sharing more information with the community about overall server load usage, costs associated with FaaS per user, and anything else valuable we can think of (except, of course, private data). The nice thing about having a real production product that\u2019s open sourced is it gives us the opportunity to talk about trials, tribulations, and issues that come up in a production app, live as they are happening.<\/p>\n<p>Stay cool, Ionic Community!<br \/>\n<a href=\"https:\/\/www.twitter.com\/jedihacks\" target=\"_blank\" rel=\"noopener\">@jedihacks<\/a><\/p>\n<p><strong><a href=\"http:\/\/try.juntoscope.com\/\">JuntoScope<\/a> is available on <a href=\"https:\/\/play.google.com\/store\/apps\/details?id=com.openforge.juntoscope\">Google Play<\/a> and the <a href=\"https:\/\/itunes.apple.com\/us\/app\/juntoscope\/id1421846154?ls=1&amp;mt=8\">App Store<\/a>.<\/strong><\/p>\n","protected":false},"excerpt":{"rendered":"<p>This is a guest post by Jedi Weller and his team at OpenForge. They have been pioneering new ways of designing digital solutions, integrating cross-disciplinary teams, and sharing common knowledge across technology companies in various verticals. Their work at OpenForge continues to lay the groundwork for clearer communication and enhanced transparency in the technology and [&hellip;]<\/p>\n","protected":false},"author":62,"featured_media":2317,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"publish_to_discourse":"","publish_post_category":"","wpdc_auto_publish_overridden":"","wpdc_topic_tags":"","wpdc_pin_topic":"","wpdc_pin_until":"","discourse_post_id":"","discourse_permalink":"","wpdc_publishing_response":"","wpdc_publishing_error":"","_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[1,121,123,124],"tags":[8],"class_list":["post-2316","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-all","category-engineering","category-perspectives","category-tutorials","tag-built-with"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v23.0 (Yoast SEO v23.0) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Built with Ionic: JuntoScope - Ionic Blog<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/ionic.io\/blog\/built-with-ionic-juntoscope\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Built with Ionic: JuntoScope\" \/>\n<meta property=\"og:description\" content=\"This is a guest post by Jedi Weller and his team at OpenForge. They have been pioneering new ways of designing digital solutions, integrating cross-disciplinary teams, and sharing common knowledge across technology companies in various verticals. Their work at OpenForge continues to lay the groundwork for clearer communication and enhanced transparency in the technology and [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/ionic.io\/blog\/built-with-ionic-juntoscope\" \/>\n<meta property=\"og:site_name\" content=\"Ionic Blog\" \/>\n<meta property=\"article:published_time\" content=\"2018-10-23T13:33:59+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2018-12-12T22:39:22+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/10\/juntoscope.png\" \/>\n\t<meta property=\"og:image:width\" content=\"512\" \/>\n\t<meta property=\"og:image:height\" content=\"250\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Matt Netkow\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@dotNetkow\" \/>\n<meta name=\"twitter:site\" content=\"@ionicframework\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Matt Netkow\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"8 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/ionic.io\/blog\/built-with-ionic-juntoscope#article\",\"isPartOf\":{\"@id\":\"https:\/\/ionic.io\/blog\/built-with-ionic-juntoscope\"},\"author\":{\"name\":\"Matt Netkow\",\"@id\":\"https:\/\/ionic.io\/blog\/#\/schema\/person\/93c8b2fe110f183510c6285b0de40790\"},\"headline\":\"Built with Ionic: JuntoScope\",\"datePublished\":\"2018-10-23T13:33:59+00:00\",\"dateModified\":\"2018-12-12T22:39:22+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/ionic.io\/blog\/built-with-ionic-juntoscope\"},\"wordCount\":1630,\"commentCount\":3,\"publisher\":{\"@id\":\"https:\/\/ionic.io\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/ionic.io\/blog\/built-with-ionic-juntoscope#primaryimage\"},\"thumbnailUrl\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/10\/juntoscope.png\",\"keywords\":[\"Built With\"],\"articleSection\":[\"All\",\"Engineering\",\"Perspectives\",\"Tutorials\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/ionic.io\/blog\/built-with-ionic-juntoscope#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/ionic.io\/blog\/built-with-ionic-juntoscope\",\"url\":\"https:\/\/ionic.io\/blog\/built-with-ionic-juntoscope\",\"name\":\"Built with Ionic: JuntoScope - Ionic Blog\",\"isPartOf\":{\"@id\":\"https:\/\/ionic.io\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/ionic.io\/blog\/built-with-ionic-juntoscope#primaryimage\"},\"image\":{\"@id\":\"https:\/\/ionic.io\/blog\/built-with-ionic-juntoscope#primaryimage\"},\"thumbnailUrl\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/10\/juntoscope.png\",\"datePublished\":\"2018-10-23T13:33:59+00:00\",\"dateModified\":\"2018-12-12T22:39:22+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/ionic.io\/blog\/built-with-ionic-juntoscope#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/ionic.io\/blog\/built-with-ionic-juntoscope\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/ionic.io\/blog\/built-with-ionic-juntoscope#primaryimage\",\"url\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/10\/juntoscope.png\",\"contentUrl\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/10\/juntoscope.png\",\"width\":512,\"height\":250},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/ionic.io\/blog\/built-with-ionic-juntoscope#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/ionic.io\/blog\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Built with Ionic: JuntoScope\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/ionic.io\/blog\/#website\",\"url\":\"https:\/\/ionic.io\/blog\/\",\"name\":\"ionic.io\/blog\",\"description\":\"Build amazing native and progressive web apps with the web\",\"publisher\":{\"@id\":\"https:\/\/ionic.io\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/ionic.io\/blog\/?s={search_term_string}\"},\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/ionic.io\/blog\/#organization\",\"name\":\"Ionic\",\"url\":\"https:\/\/ionic.io\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/ionic.io\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/10\/white-on-color.png\",\"contentUrl\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/10\/white-on-color.png\",\"width\":1920,\"height\":854,\"caption\":\"Ionic\"},\"image\":{\"@id\":\"https:\/\/ionic.io\/blog\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/x.com\/ionicframework\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/ionic.io\/blog\/#\/schema\/person\/93c8b2fe110f183510c6285b0de40790\",\"name\":\"Matt Netkow\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/ionic.io\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/07\/mattnetkow-150x150.jpg\",\"contentUrl\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/07\/mattnetkow-150x150.jpg\",\"caption\":\"Matt Netkow\"},\"sameAs\":[\"https:\/\/x.com\/dotNetkow\"],\"url\":\"https:\/\/ionic.io\/blog\/author\/mattnetkow\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Built with Ionic: JuntoScope - Ionic Blog","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/ionic.io\/blog\/built-with-ionic-juntoscope","og_locale":"en_US","og_type":"article","og_title":"Built with Ionic: JuntoScope","og_description":"This is a guest post by Jedi Weller and his team at OpenForge. They have been pioneering new ways of designing digital solutions, integrating cross-disciplinary teams, and sharing common knowledge across technology companies in various verticals. Their work at OpenForge continues to lay the groundwork for clearer communication and enhanced transparency in the technology and [&hellip;]","og_url":"https:\/\/ionic.io\/blog\/built-with-ionic-juntoscope","og_site_name":"Ionic Blog","article_published_time":"2018-10-23T13:33:59+00:00","article_modified_time":"2018-12-12T22:39:22+00:00","og_image":[{"width":512,"height":250,"url":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/10\/juntoscope.png","type":"image\/png"}],"author":"Matt Netkow","twitter_card":"summary_large_image","twitter_creator":"@dotNetkow","twitter_site":"@ionicframework","twitter_misc":{"Written by":"Matt Netkow","Est. reading time":"8 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/ionic.io\/blog\/built-with-ionic-juntoscope#article","isPartOf":{"@id":"https:\/\/ionic.io\/blog\/built-with-ionic-juntoscope"},"author":{"name":"Matt Netkow","@id":"https:\/\/ionic.io\/blog\/#\/schema\/person\/93c8b2fe110f183510c6285b0de40790"},"headline":"Built with Ionic: JuntoScope","datePublished":"2018-10-23T13:33:59+00:00","dateModified":"2018-12-12T22:39:22+00:00","mainEntityOfPage":{"@id":"https:\/\/ionic.io\/blog\/built-with-ionic-juntoscope"},"wordCount":1630,"commentCount":3,"publisher":{"@id":"https:\/\/ionic.io\/blog\/#organization"},"image":{"@id":"https:\/\/ionic.io\/blog\/built-with-ionic-juntoscope#primaryimage"},"thumbnailUrl":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/10\/juntoscope.png","keywords":["Built With"],"articleSection":["All","Engineering","Perspectives","Tutorials"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/ionic.io\/blog\/built-with-ionic-juntoscope#respond"]}]},{"@type":"WebPage","@id":"https:\/\/ionic.io\/blog\/built-with-ionic-juntoscope","url":"https:\/\/ionic.io\/blog\/built-with-ionic-juntoscope","name":"Built with Ionic: JuntoScope - Ionic Blog","isPartOf":{"@id":"https:\/\/ionic.io\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/ionic.io\/blog\/built-with-ionic-juntoscope#primaryimage"},"image":{"@id":"https:\/\/ionic.io\/blog\/built-with-ionic-juntoscope#primaryimage"},"thumbnailUrl":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/10\/juntoscope.png","datePublished":"2018-10-23T13:33:59+00:00","dateModified":"2018-12-12T22:39:22+00:00","breadcrumb":{"@id":"https:\/\/ionic.io\/blog\/built-with-ionic-juntoscope#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/ionic.io\/blog\/built-with-ionic-juntoscope"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/ionic.io\/blog\/built-with-ionic-juntoscope#primaryimage","url":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/10\/juntoscope.png","contentUrl":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/10\/juntoscope.png","width":512,"height":250},{"@type":"BreadcrumbList","@id":"https:\/\/ionic.io\/blog\/built-with-ionic-juntoscope#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/ionic.io\/blog"},{"@type":"ListItem","position":2,"name":"Built with Ionic: JuntoScope"}]},{"@type":"WebSite","@id":"https:\/\/ionic.io\/blog\/#website","url":"https:\/\/ionic.io\/blog\/","name":"ionic.io\/blog","description":"Build amazing native and progressive web apps with the web","publisher":{"@id":"https:\/\/ionic.io\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/ionic.io\/blog\/?s={search_term_string}"},"query-input":"required name=search_term_string"}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/ionic.io\/blog\/#organization","name":"Ionic","url":"https:\/\/ionic.io\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/ionic.io\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/10\/white-on-color.png","contentUrl":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/10\/white-on-color.png","width":1920,"height":854,"caption":"Ionic"},"image":{"@id":"https:\/\/ionic.io\/blog\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/x.com\/ionicframework"]},{"@type":"Person","@id":"https:\/\/ionic.io\/blog\/#\/schema\/person\/93c8b2fe110f183510c6285b0de40790","name":"Matt Netkow","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/ionic.io\/blog\/#\/schema\/person\/image\/","url":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/07\/mattnetkow-150x150.jpg","contentUrl":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/07\/mattnetkow-150x150.jpg","caption":"Matt Netkow"},"sameAs":["https:\/\/x.com\/dotNetkow"],"url":"https:\/\/ionic.io\/blog\/author\/mattnetkow"}]}},"jetpack_sharing_enabled":true,"jetpack_featured_media_url":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/10\/juntoscope.png","_links":{"self":[{"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/posts\/2316","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/users\/62"}],"replies":[{"embeddable":true,"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/comments?post=2316"}],"version-history":[{"count":0,"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/posts\/2316\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/media\/2317"}],"wp:attachment":[{"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/media?parent=2316"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/categories?post=2316"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/tags?post=2316"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}