DEV Community: Dane HillardThe latest articles on DEV Community by Dane Hillard (@easyaspython).
https://dev.to/easyaspython
https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F119031%2F60e98f61-92c1-481a-9c0b-44f94bf96d05.jpgDEV Community: Dane Hillard
https://dev.to/easyaspython
enChoose Boring ReleasesDane HillardThu, 26 Sep 2024 16:52:04 +0000
https://dev.to/ithaka/choose-boring-releases-3h7k
https://dev.to/ithaka/choose-boring-releases-3h7k<p>Academic research increasingly relies on diverse content types, including gray literature and primary source materials, alongside traditional peer-reviewed works. ITHAKA supports this evolving research landscape through initiatives that foster cross-content connections on <a href="proxy.php?url=https://www.jstor.org/" rel="noopener noreferrer">JSTOR</a>, our digital library that supports research, teaching, and learning. These include <a href="proxy.php?url=https://www.about.jstor.org/whats-in-jstor/infrastructure/" rel="noopener noreferrer">infrastructure services</a> that enable institutions to make their digital archives and special collections discoverable on the platform, and a years-long effort to integrate Artstor — a vast collection of images and multimedia for educational use — onto the JSTOR platform.</p>
<p><a href="proxy.php?url=https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj66dv73cd7vhuj2wmu1k.jpeg" class="article-body-image-wrapper"><img src="proxy.php?url=https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj66dv73cd7vhuj2wmu1k.jpeg" width="800" height="610"></a><br>
<em>Albrecht Dürer (German, Nuremberg 1471–1528 Nuremberg). “Alberti Dvreri Pictoris et Architecti Praestantissimi De Vrbibvs…” 1535. Illustrated book, 78 pp.; H: 13 3/4 in. (35 cm). The Metropolitan Museum of Art. <a href="proxy.php?url=https://jstor.org/stable/community.34718827" rel="noopener noreferrer">https://jstor.org/stable/community.34718827</a>. Just one of the millions of items now available on JSTOR.</em></p>
<p>On August 1, 2024, the <a href="proxy.php?url=https://www.about.jstor.org/news/jstor-announces-artstor-on-jstor/" rel="noopener noreferrer">Artstor migration to the JSTOR platform</a> culminated in the need to smoothly guide users from the Artstor Image Workspace (AIW) at library.artstor.org to appropriate counterparts on <a href="proxy.php?url=http://www.jstor.org" rel="noopener noreferrer">www.jstor.org</a>. The amount of work this required was daunting; the AIW platform was as large and complex as JSTOR, and covered many different product areas with ownership spread across several different teams. To achieve this transition without drawing focus away from other objectives, we had to adopt a strategic approach.</p>
<h3>
The challenge
</h3>
<p>Our two main constituents for this transition were our users — primarily art history researchers and faculty — and search engine web crawlers. Like JSTOR users, many Artstor users start their searches outside of our platforms and need to be able to find things no matter what platform changes we make.</p>
<p>The first piece of the challenge was technical: Client-side routing was used extensively on the AIW platform. That routing wasn’t built with web crawlers in mind, so many modern web crawlers couldn’t handle it very well. Plus, these client-side routes were generally invisible to our servers because they used hash-based routing rather than the history API. Some pages did support server-side rendering so web crawlers could index them, which gave us an additional scenario to support.</p>
<p>The second challenge was human: Domain knowledge about the details of all this routing and the user value of various pages was spread throughout the organization, and the people with the domain knowledge didn’t always have the technical knowledge to deal with the redirects.</p>
<p>Finally, there was the sheer scope of the work to be done. AIW itself was a huge platform, but on top of that we also had to redirect supporting areas like the marketing and support sites. Very early on, we had to set some parameters to rein in the scope:</p>
<ul>
<li>We wouldn’t guarantee or aim for 100% of traffic to be redirected. Some pages and requests simply couldn’t be redirected anywhere reasonable, and redirecting to generic informational pages can lead to search engine ranking penalties. More importantly for our user focus, it would be disorienting to click a bookmark or a search engine result and get redirected to something irrelevant.</li>
<li>We would create a central, extensible service designed for loose coupling of the URL patterns to match and the behavior when such a match occurred. This would reduce unnecessary coupling and dependencies between teams as well as the context those teams had to hold onto during the migration project.</li>
<li>We would enable teams to implement redirects for the broadest-used page types (such as individual items), but would equally enable implementing redirects for the long tail of irregular page types. This would create a familiar implementation pattern for teams that they wouldn’t have to abandon for more exotic use cases.</li>
<li>We would enable visibility into the behavior of the system as early as possible, so that we could observe the trend over time. This fostered an experimentation mindset: We could develop hypotheses about how the traffic would be handled, implement a change, and quickly confirm or disconfirm the desired effect after deploying the new change. This also gave us opportunities to leverage parts of our deployment platform we hadn’t exercised much previously, adding some greenfield work to an otherwise brownfield kind of activity.</li>
</ul>
<p>So instead of perfection, we defined our scope as removing as much risk as possible and building in as much confidence as we could ahead of our launch date. Our goal was to coast through the finish line instead of scrambling.</p>
<h3>
The approach
</h3>
<p>So how did we do that? A few key tactics showed up for us and made things possible throughout this project.</p>
<h4>
Practice product-minded engineering
</h4>
<p>We approached the project using product-minded engineering principles, which leverage user-focused agile techniques and strong cross-disciplinary and interdisciplinary skills to identify problems worth solving. By considering both user needs and architectural needs together, we came away with a more globally maximum solution without oversimplifying or overengineering.</p>
<h4>
Use traffic shadowing
</h4>
<p>Traffic shadowing allows inbound requests to be sent to two or more destinations, each able to take some action. Although one system is still responsible for sending a response back to the user, other systems can also make observations about or take action on requests. Our Capstan platform made this easy to try, and it worked very well. We continued serving AIW pages to users while also sending requests to our new redirect service, so we could see how it would behave and perform.</p>
<h4>
Measure early and often
</h4>
<p>We built metrics into the redirect service so that as we started implementing and deploying redirect behaviors we could assess the total traffic volume and understand what portion of that traffic was being handled by a known redirect. That was a huge confidence builder, as we could actually demonstrate not only that the service <em>should</em> redirect the implemented URLs once turned on, but that <em>it was in fact doing so</em> — with the shadow traffic.</p>
<h4>
Use feature flags
</h4>
<p>Where we couldn’t implement traffic shadowing for some supporting pages, we used <a href="proxy.php?url=https://medium.com/ithaka-tech/deploying-features-under-cover-of-darkness-f112ce444bba" rel="noopener noreferrer">feature flagging</a>. With feature flags, we can ship two different behaviors and then allow staff and automated tests to toggle those behaviors on or off before exposing that new behavior to users. Even under normal circumstances we have many teams developing across many areas of the platform, and a feature flagging strategy brings <a href="proxy.php?url=https://medium.com/ithaka-tech/find-bugs-before-your-users-do-closing-the-software-development-risk-exposure-gap-cace5dbd19d2" rel="noopener noreferrer">speed and safety</a> by ensuring we don’t have to perform a “big bang” release at the very end of a project.</p>
<h4>
Think in transition architectures
</h4>
<p>In effect, all of the redirect architecture we built to support this migration was a transition architecture. But we also built some smaller transition systems to move the control of traffic from external vendors into our own platform, making it easier to flip a switch on release day instead of having to handle several moving parts in a row.</p>
<p>Easing the transition architecture burden made a big difference on several of our support sites. For example, the DNS records for these sites were initially pointed directly at third-party vendors; before we could control the traffic, the traffic would have to come to us. If we waited until release day for that to happen, the DNS change could take longer than expected to propagate, or it could result in unforeseen consequences, or the logic we implemented for it could be wrong. Instead, we designed these constraints away. We worked to bring the DNS under our control earlier, and although it initially continued to point to the third-party vendors, we could now make immediate changes to its behavior.</p>
<h4>
Create a loosely coupled design
</h4>
<p>Because we had to collect and implement so much widely scattered domain knowledge into these redirects, we had to build a system that made it easy to contribute and hard to cross wires or step on other people’s toes. The system we created allowed teams to come in with nothing more than a URL pattern to match on AIW and a destination URL to send the traffic to. If necessary, they could also contact downstream services for data to help them decide where to redirect that traffic.</p>
<p>Basically, we decided to meet teams where they are, with their domain knowledge, so they didn’t have to think too much about the technical aspects of how redirection works. This also allowed the team working on the redirect service to do that work without having to worry too much about the domain knowledge. This separated responsibility in a nice way and ensured better outcomes for us and our users.</p>
<h4>
Have regular, valuable trade-off discussions
</h4>
<p>Finally, we engaged throughout this whole project in high-quality trade-off discussions. We talked about the level of effort a particular change required compared to the expected user impact. We talked about specific pages and how to handle them. And because we had such a rich cross-section of technical and domain knowledge in those discussions, we made better decisions about how to simplify things or adjust to reduce our overall risk and costs.</p>
<p>This is really a reprise of product-minded engineering, but this highlights one of its most impactful outcomes — giving us context about what’s possible, what gives us the best ROI, and how we might deliver better outcomes for us and users.</p>
<h3>
The result
</h3>
<p>We worked hard leading up to our launch date, August 1. But release day itself was one of the most boring of all time. We did about 30 minutes of real work to flip on some of those feature flags and deploy a couple of applications, all of which we were highly confident in because of our previous metrics and testing. In that short time we started redirecting over 95% of all AIW traffic to known locations on JSTOR and its supporting sites. The remaining requests were for things we had intentionally decided not to handle.</p>
<p>We only shipped one bug that we know of, which we spotted quickly. Because of our system design we were able to revert immediately to the old system while we worked on a short-term fix, and within a couple of weeks put a long-term fix in place so we could sunset the old system.</p>
<p>Meanwhile, we’re listening for user experience feedback in case anyone is confused or notices broken redirects, and we’re watching how our search engine crawling and ranking responds. As Google and Bing and others recrawl our content, they’ll start sending people directly to JSTOR. <a href="proxy.php?url=https://groups.niso.org/higherlogic/ws/public/download/26321" rel="noopener noreferrer">NISO recommends</a> leaving these redirects in place for at least a year, so we’ve committed to that. Next year we’ll evaluate whether this architecture has run its course and act accordingly.</p>
<h3>
The takeaways
</h3>
<p>While the technical tools and architecture we used were obviously important, adopting a loosely coupled, extensible design was key to our success. By bringing solutions to teams that allowed them to focus on their domain knowledge we played to everyone’s strengths. It also ensured teams weren’t colliding with each other too often. This approach helped us focus and synthesize everyone’s strengths. It may not be possible in all cases, but for this project it was one of our best and most productive decisions.</p>
<p>By thinking in transition architectures and building key visibility into the system at the outset, we were able to create significant confidence in our progress and readiness for release day, making the finale just a blip on the radar. This raised an unexpected consequence — it would have been easy to say, “Look, we did the thing,” and part ways on release day, because it was so uneventful. Choose boring releases, but be sure to keep the afterparty exciting.</p>
<p>Interested in learning more about working at ITHAKA? Contact recruiting to learn more about <a href="proxy.php?url=https://www.ithaka.org/careers/?gh_src=1ba9e9eb5us" rel="noopener noreferrer">ITHAKA tech jobs</a>.</p>
integrationtestingreleasemanagementagilemigrationI'm writing Publishing Python PackagesDane HillardSun, 14 Nov 2021 01:37:35 +0000
https://dev.to/easyaspython/im-writing-publishing-python-packages-1hnh
https://dev.to/easyaspython/im-writing-publishing-python-packages-1hnh<p>This is my first DEV post in quite a while. I've been really busy. In 2020 in the midst of the pandemic we purchased a home across the state, renovated it, and moved. Not long after getting settled into that, I jumped into writing another book.</p>
<p>Over the last several years at <a href="proxy.php?url=https://www.ithaka.org">ITHAKA</a> I've focused on creating modular code shared across our department and platform, maintaining a few dozen Python and JavaScript packages. I've also come to love the open source community—though I don't contribute nearly as often as I'd like—and thinking about ways to solve some the issues project maintainers face in their day-to-day work.</p>
<p>I hope that my new book, <a href="proxy.php?url=https://pypackages.com"><em>Publishing Python Packages</em></a>, can make even a small impact in helping project maintainers be more productive through tooling and automation, so they can continue focusing on their projects and communities.</p>
<p><a href="proxy.php?url=https://pypackages.com"><img src="proxy.php?url=https://res.cloudinary.com/practicaldev/image/fetch/s--JwEloE28--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xvdhl0l6pzr5h1d7wz32.jpg" alt="Publishing Python Packages by Dane Hillard for Manning Books" width="264" height="331"></a></p>
<p><a href="proxy.php?url=https://manning.com">Manning</a> has made a <strong>50% discount</strong> code for the book available, and if you've been thinking about grabbing a copy or know someone who might benefit from it, I hope you'll be able to take advantage of this price reduction while it's in early access. Use the code <code>mlhillard350</code> at checkout.</p>
<p>I'm happy to answer any questions I can about the book, the publishing process, or my writing workflow as well! Hope to hear from you ☺️</p>
pythonbookshowdevdiscussPractices of the Python Pro out now!Dane HillardSat, 11 Jan 2020 17:02:05 +0000
https://dev.to/easyaspython/practices-of-the-python-pro-out-now-383p
https://dev.to/easyaspython/practices-of-the-python-pro-out-now-383p<p>Nearly a year ago I announced that I was writing a book about writing Python code.</p>
<div class="ltag__link">
<a href="proxy.php?url=/easyaspython" class="ltag__link__link">
<div class="ltag__link__pic">
<img src="proxy.php?url=https://res.cloudinary.com/practicaldev/image/fetch/s--Ecl7bZn4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--PyAr_1m8--/c_fill%2Cf_auto%2Cfl_progressive%2Ch_150%2Cq_auto%2Cw_150/https://dev-to-uploads.s3.amazonaws.com/uploads/user/profile_image/119031/60e98f61-92c1-481a-9c0b-44f94bf96d05.jpg" alt="easyaspython">
</div>
</a>
<a href="proxy.php?url=/easyaspython/im-writing-code-like-a-pro-software-development-in-python-39ji" class="ltag__link__link">
<div class="ltag__link__content">
<h2>I'm writing Practices of the Python Pro</h2>
<h3>Dane Hillard ・ Jan 31 '19 ・ 2 min read</h3>
<div class="ltag__link__taglist">
<span class="ltag__link__tag">#python</span>
<span class="ltag__link__tag">#discuss</span>
<span class="ltag__link__tag">#beginners</span>
<span class="ltag__link__tag">#showdev</span>
</div>
</div>
</a>
</div>
<p><em>Practices of the Python Pro</em> is <a href="proxy.php?url=https://thepythonpro.com">now available</a>! Holding the physical copy in my hand for the first time was a wild experience. It didn't quite become real until that moment.</p>
<p>I set out to fill what I saw as a learning gap for many people entering the software development space today. Some of my friends were either self-taught programmers who owned their own businesses, trying to pick up programming for use with their existing career, or trying to switch careers into software. The diversity of ways people are getting into and using software is growing so fast!</p>
<p>Even as someone with a traditional path into software development, I didn't learn many of the principles in school that lead to more maintainable code. Learning how to separate concerns, use composition over inheritance, and create more extensible code with approaches like plugin-based architecture all happened "in the field" for me. For those with even less exposure to this stuff, it can be hard to even know these options are out there.</p>
<p><em>Practices of the Python Pro</em> seeks to give an introduction to many of these principles, building a small project that takes advantage of them along the way. My hope is that it demonstrates the values of maintainable code enough to excite you to keep researching and learning more on your own for years to come.</p>
<p>I learned a lot myself in writing this book. Taking what I already knew theoretically and forcing myself to apply it practically to teach others about it was a great way to find gaps in my own knowledge and realize my inexperience in some areas. I think that's the value of writing and the value of platforms like DEV where people can safely explore and discuss ideas with others. Thanks to all of you for such a lovely space in this corner of the web!</p>
pythonshowdevcodequalitybooksPractices of the Python Pro is on Product HuntDane HillardMon, 28 Oct 2019 18:16:57 +0000
https://dev.to/easyaspython/practices-of-the-python-pro-is-on-product-hunt-3bla
https://dev.to/easyaspython/practices-of-the-python-pro-is-on-product-hunt-3bla<p>A while ago I shared about the book I was writing on software development in Python, and received such a great outpouring of support from the DEV community! Thank you all 💖</p>
<div class="ltag__link">
<a href="proxy.php?url=/easyaspython" class="ltag__link__link">
<div class="ltag__link__pic">
<img src="proxy.php?url=https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F119031%2F60e98f61-92c1-481a-9c0b-44f94bf96d05.jpg" alt="easyaspython">
</div>
</a>
<a href="proxy.php?url=/easyaspython/im-writing-code-like-a-pro-software-development-in-python-39ji" class="ltag__link__link">
<div class="ltag__link__content">
<h2>I'm writing Practices of the Python Pro</h2>
<h3>Dane Hillard ・ Jan 31 '19</h3>
<div class="ltag__link__taglist">
<span class="ltag__link__tag">#python</span>
<span class="ltag__link__tag">#discuss</span>
<span class="ltag__link__tag">#beginners</span>
<span class="ltag__link__tag">#showdev</span>
</div>
</div>
</a>
</div>
<p>Today, with the book nearing the finish line in production and with all content available in the early access version, I <a href="proxy.php?url=https://www.producthunt.com/posts/practices-of-the-python-pro" rel="noopener noreferrer">launched the book on Product Hunt</a>! If you have the time and interest to join the discussion, leave a review (if you've read the book), or share the link around, please do! It would mean the world to me.</p>
<p><a href="proxy.php?url=https://www.producthunt.com/posts/practices-of-the-python-pro" rel="noopener noreferrer"><img src="proxy.php?url=https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F8t0co0r75b7wstgq946z.png" alt="Product Hunt badge"></a></p>
<p>My availability to write online or do things for fun has been a bit stagnant for some time, but I hope that I'll be able to start getting back to it soon with a few things coming to a close. I look forward to seeing you all again in no time!</p>
beginnerspythoneducationarchitectureLearn the Django User Authentication SystemDane HillardWed, 07 Aug 2019 15:46:20 +0000
https://dev.to/easyaspython/learn-the-django-user-authentication-system-36pl
https://dev.to/easyaspython/learn-the-django-user-authentication-system-36pl<h2>
Introduction
</h2>
<p>Giving users the ability to create an account they can sign into is a common function for many websites.</p>
<p>Users might need an account to participate in a comment thread, save their personal information, or transfer money. Whatever the use case may be, you need to build an authentication system that’s simple and safe for your users.</p>
<p>After reading this post, you should have a solid understanding of how Django thinks about authentication – from users, to groups, to permissions. You’ll also see how Django plays things safe where it can in order to help you avoid inadvertently contributing your users’ information to "<a href="proxy.php?url=https://haveibeenpwned.com/">Have I Been Pwned</a>".</p>
<h2>
Users
</h2>
<p>For most websites, the basic entity of authentication is a user. A user is identified by some unique string, which is almost always an email address or username.</p>
<p>To prove someone is who they say they are, they must provide a password when creating an account, and again at any time they want to authenticate themselves. This should be familiar: you go through this kind of workflow any time you sign up for a service like Twitter or Netflix.</p>
<p>Django provides a User model for creating and managing users. <a href="proxy.php?url=https://kite.com/python/docs/django.contrib.auth.models.User">Django users</a> have a username and password, but can also optionally have an email address and a first and last name:<br>
</p>
<div class="highlight js-code-highlight">
<pre class="highlight python"><code><span class="kn">from</span> <span class="nn">django.contrib.auth.models</span> <span class="kn">import</span> <span class="n">User</span>
<span class="n">rafaela</span> <span class="o">=</span> <span class="n">User</span><span class="p">(</span><span class="s">'rafaela'</span><span class="p">,</span> <span class="n">password</span><span class="o">=</span><span class="s">'$uper$ecretpassword'</span><span class="p">)</span>
<span class="c1"># OR
</span>
<span class="n">rafaela</span> <span class="o">=</span> <span class="n">User</span><span class="p">(</span>
<span class="s">'Rafaela'</span><span class="p">,</span>
<span class="n">email</span><span class="o">=</span><span class="s">'[email protected]'</span><span class="p">,</span>
<span class="n">password</span><span class="o">=</span><span class="s">'$upser$ecretpassword'</span><span class="p">,</span>
<span class="n">first_name</span><span class="o">=</span><span class="s">'Rafaela'</span><span class="p">,</span>
<span class="n">last_name</span><span class="o">=</span><span class="s">'Lòpez'</span><span class="p">,</span>
<span class="p">)</span>
</code></pre>
</div>
<p>If you prefer to identify users by their email addresses, I recommend filling the username with the email address and keeping the address in the email field as well. This will allow users to authenticate using their email address while also allowing you to continue using Django’s built-in features that deal with email.</p>
<p>Django provides a level of security when it comes to passwords. It has a built-in set of password validators, some of which are enabled by default in new projects. You can write your own validators to enforce any password rules you might need, but choose wisely – it’s been shown that <a href="proxy.php?url=https://www.riskcontrolstrategies.com/2018/01/08/new-nist-guidelines-wrong/">many password rules lead to decreased security</a>!</p>
<p>In addition to password validation, Django safely stores password information by default. Django salts and hashes passwords before storing them when a user is created, so their plaintext password is no longer available outside the context of the initial registration request or when they log in.</p>
<p>Storing passwords in plaintext is a surprisingly common oversight in the industry, so let Django be your safety rail here!</p>
<p>Like other models you may have used in Django, user objects can be queried and filtered and so on:<br>
</p>
<div class="highlight js-code-highlight">
<pre class="highlight python"><code><span class="n">User</span><span class="p">.</span><span class="n">objects</span><span class="p">.</span><span class="nb">filter</span><span class="p">(</span><span class="n">first_name</span><span class="o">=</span><span class="s">'Rafaela'</span><span class="p">)</span>
</code></pre>
</div>
<p>User objects have several other fields, attributes, and methods that will make sense in context as you read on about the Django features that involve users.</p>
<p>Read more about groups, permissions, and more in <a href="proxy.php?url=https://kite.com/blog/python/django-authentication/">the full article</a>!</p>
djangowebdevsecuritypythonOur Open Source Software in the WildDane HillardFri, 21 Jun 2019 17:20:42 +0000
https://dev.to/ithaka/our-open-source-software-in-the-wild-3kh2
https://dev.to/ithaka/our-open-source-software-in-the-wild-3kh2<p><a href="proxy.php?url=https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F882%2F1%2A_COUuOHLmKfILvbwWp873w.jpeg" class="article-body-image-wrapper"><img src="proxy.php?url=https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F882%2F1%2A_COUuOHLmKfILvbwWp873w.jpeg"></a></p>
<p><a href="proxy.php?url=https://www.ithaka.org/" rel="noopener noreferrer">ITHAKA</a> has a mission to make knowledge widely available, accessible, and sustainable. This mission runs parallel to the open source approach to software development. In making software, systems, and methodologies public, organizations enable others to build on their successes as well as critically examine these artifacts for bugs or security flaws.</p>
<p>The collaborative nature that open source strives toward acts as a tide that raises all ships; the community can produce improvements that benefit everyone. These software changes are tracked over time and the historical record that arises also acts as an audit trail for reproducibility or as a catalyst for someone hoping to learn how something is done.</p>
<p>Our front-end developers that work on <a href="proxy.php?url=https://www.jstor.org/" rel="noopener noreferrer">JSTOR</a> consume data from a myriad of back-end systems, from those we manage in-house for authentication and search to third-party APIs for fetching data from Wikipedia. As the list of services we interact with grew over the last few years, we found it difficult to manage the services, let alone all the endpoints we communicate with in each of them. We built a <a href="proxy.php?url=https://www.python.org/" rel="noopener noreferrer">Python</a> project, <a href="proxy.php?url=https://github.com/ithaka/apiron" rel="noopener noreferrer">ithaka/apiron</a>, in order to ease this burden. Sometime soon after that, we decided that apiron might be useful enough to others to warrant publication and got to work on all the legal and marketing threads involved.</p>
<p>Since open sourcing apiron, it’s seen moderate but intriguing interest from the Python community. Jeff Triplett, a Director of the Python Software Foundation, was <a href="proxy.php?url=https://github.com/ithaka/apiron/pull/12" rel="noopener noreferrer">the first third-party contributor</a> to the apiron project. Jeff lives in Lawrence, KS, the birthplace of the <a href="proxy.php?url=https://www.djangoproject.com/" rel="noopener noreferrer">Django web framework</a> that we make heavy use of on JSTOR. He organizes the DjangoCon US conference as the president and co-founder of the Django Events Foundation North America.</p>
<p><a href="proxy.php?url=https://github.com/ithaka/apiron/pull/14" rel="noopener noreferrer">Another third-party contributor</a>, who we know only by the cryptic handle teffalump, used apiron to build a Python package for communicating with servers managing DICOM, a standard for working with medical imaging records. Excitingly, their work went on to be <a href="proxy.php?url=https://github.com/brown-bnc/bnctools" rel="noopener noreferrer">used by folks</a> in the Brown University Behavioral and Neuroimaging team.</p>
<p>One beautiful consequence of the freedom of open source software is that you never quite know where it will end up. We built apiron to improve how we talk to a search service, and already it’s spread to use cases we couldn’t have imagined at the outset if we’d tried. Exploring these use cases as opportunities to learn will ultimately help us improve our software and ourselves.</p>
<p>As ITHAKA publishes software that generates reach and impact, those we engage with may develop an interest in working with us more directly, perhaps as collaborators or even as employees It’s also a new way to pursue our mission to expand access to knowledge (in all its forms).</p>
<p><em>If you’re curious about the steps involved in open sourcing projects, my experience with it, how to effectively manage an open source project, or are thinking about using apiron, please leave a comment or reach out to me on Twitter at</em> <a href="proxy.php?url=https://twitter.com/easyaspython" rel="noopener noreferrer"><em>@easyaspython</em></a><em>!</em></p>
softwaredevelopmentopensourcewebdevpythonForm habits by making the right thing easiestDane HillardThu, 25 Apr 2019 19:09:10 +0000
https://dev.to/easyaspython/form-habits-by-making-the-right-thing-easiest-p75
https://dev.to/easyaspython/form-habits-by-making-the-right-thing-easiest-p75<p>I'm a creature of habit. I tend to configure things the way I like them, lock that in, and hunker down into the little ecosystem I've made for myself forevermore. When something arises and begins causing friction, my only hope of adapting effectively is to find a new configuration that I can quickly cut over to.</p>
<p>For some time, I've made git commits using a command you're probably used to seeing:<br>
</p>
<div class="highlight js-code-highlight">
<pre class="highlight plaintext"><code>$ git commit --all --message 'Make the code less wonky'
</code></pre>
</div>
<p>This works alright, but I had this undercurrent of unease about it for the last while. <code>git</code> allows you to specify longer descriptions by formatting your messages a certain way. When you push that commit to tools like GitHub or GitLab and open a pull request, they'll use the short description as the title and the longer description will get plugged into the pull request's description field. Providing this is a matter of strategically adding some newlines to your commit messages, but I often found myself accidentally closing the quotes on the first line of the message or just plain forgetting to provide the long-form description. This was friction.<br>
</p>
<div class="highlight js-code-highlight">
<pre class="highlight plaintext"><code>$ git commit --all --message 'Make the code less wonky
* Fix a bug
* Add some comments
* Remove bitcoin mining malware'
</code></pre>
</div>
<p>GitHub also recently added a feature for <a href="proxy.php?url=https://help.github.com/en/articles/creating-a-commit-on-behalf-of-an-organization">making commits "on behalf of" an organization</a> — useful if you contribute to open source as part of your day-to-day work at your company. Part of this feature involves adding even more information to the commit, with more newlines to remember:<br>
</p>
<div class="highlight js-code-highlight">
<pre class="highlight plaintext"><code>$ git commit --all --message 'Add a bit of wonk back in
* We want those bitcoins so add the malware back
* That "bug" was a "feature" it turns out
on-behalf-of: @some-org <[email protected]>'
</code></pre>
</div>
<p>This gets pretty unruly, especially if you're trying to <a href="proxy.php?url=https://sethrobertson.github.io/GitBestPractices/#commit">commit early and often</a> (something you should do!). I really wanted to start making more descriptive commits, and wanted to have the proper attribution in open source, but this is a lot of friction. Fortunately, <code>git</code> provided a solution that allowed me to adapt.</p>
<p>You can configure <code>git</code> to use a template for your commits, which can be stored in a file. When you don't provide a <code>--message</code> argument at the command line, <code>git</code> will open a text editor where you can edit the commit message, pre-populated with the content of your template. I figured this could be a great way to simplify some of my workflow and prompt me to add more descriptive content to my commits. This automatic prompting is small effort but adds a bit of productivity <em>and</em> improves the information I communicate to others, so it's a big win.</p>
<p>I started by creating the template that fit the commit format to which I aspired:<br>
</p>
<div class="highlight js-code-highlight">
<pre class="highlight plaintext"><code>Summary of change
Longer description of change
on-behalf-of: @some-org <[email protected]>
</code></pre>
</div>
<p>And then I told <code>git</code> to use it for the repositories I was interested in:<br>
</p>
<div class="highlight js-code-highlight">
<pre class="highlight plaintext"><code>$ git config commit.template /path/to/commit.template
</code></pre>
</div>
<p>That's about it in terms of setup! But I was able to start reaping its benefits immediately; my command to invoke the commit process is shorter without the <code>--message</code> flag (and the message that follows), and the process of thinking about what to say <em>about</em> the commit is now separate, which gives me a chance to pause and think of something more valuable to say than just <a href="proxy.php?url=https://xkcd.com/1296/">haaaaaands</a>.</p>
<p>In case I'm feeling lazy or forgetful, the template also has my back and reminds me that adding context is good to do. It also always has the <code>on-behalf-of</code> commit trailer, so I barely need to think about it. Overall this has saved me a lot of cumulative time, and has made me more thoughtful about what I'm sharing and putting out there. Even on repositories where I don't have this configured (because the configuration has different requirements and I haven't gotten around to it yet), I've at least switched to always using a text editor to edit my commit messages.</p>
<p>So the moral of this story isn't about <code>git</code>. Making the "right" thing the easiest possible option is the way I can truly form habits. If you're like me, if two things are equally easy I'll flip flop on them until I drive myself up the wall, and if the "wrong" thing is easiest I'm doomed. So go out there and squeeze some productivity out of your tooling by making it work for you!</p>
<p>What are your best tool configuration or productivity wins?</p>
productivitybeginnersgitdiscussA flexible approach to Python API client developmentDane HillardFri, 12 Apr 2019 01:13:05 +0000
https://dev.to/easyaspython/a-flexible-approach-to-python-api-client-development-50lb
https://dev.to/easyaspython/a-flexible-approach-to-python-api-client-development-50lb<p><a href="proxy.php?url=https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F0mlhix21sasx9lzqlfdf.png" class="article-body-image-wrapper"><img src="proxy.php?url=https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F0mlhix21sasx9lzqlfdf.png" alt="Ditto Pokémon"></a></p>
<p>Are you working in a microservice-oriented architecture? Is there an API you want to use that doesn't have a Python SDK? Although high-level HTTP libraries like <code>requests</code> aid development by reducing the work you need to do to get up and running, they don't provide much structure around APIs specifically. This can lead to several pieces of similar calling code peppered throughout your system, in the worst cases leading to divergent approaches.</p>
<p>Let's take a look at what a set of API calls could look like using <code>requests</code>. I'll use the PokéAPI, a fantastic (and free!) API for querying Pokémon metadata. The most useful endpoint is <code>/pokemon/:pokemon</code>, which is the main entrypoint for getting information about a particular Pokémon. You can call this endpoint using <code>requests</code>:<br>
</p>
<div class="highlight js-code-highlight">
<pre class="highlight python"><code><span class="o">>>></span> <span class="kn">import</span> <span class="n">requests</span>
<span class="o">>>></span> <span class="n">ditto</span> <span class="o">=</span> <span class="n">requests</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="sh">'</span><span class="s">https://pokeapi.co/api/v2/pokemon/ditto</span><span class="sh">'</span><span class="p">)</span>
</code></pre>
</div>
<p>This returns a bevy of information, much of which links to other endpoints. To inspect the data more easily, you'll want to get the returned JSON as a Python dictionary:<br>
</p>
<div class="highlight js-code-highlight">
<pre class="highlight python"><code><span class="o">>>></span> <span class="n">ditto</span> <span class="o">=</span> <span class="n">requests</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="sh">'</span><span class="s">https://pokeapi.co/api/v2/pokemon/ditto</span><span class="sh">'</span><span class="p">).</span><span class="nf">json</span><span class="p">()</span>
</code></pre>
</div>
<p>You can verify you've received a response about Ditto by checking its name:<br>
</p>
<div class="highlight js-code-highlight">
<pre class="highlight python"><code><span class="o">>>></span> <span class="n">ditto</span><span class="p">[</span><span class="sh">'</span><span class="s">name</span><span class="sh">'</span><span class="p">]</span>
<span class="sh">'</span><span class="s">ditto</span><span class="sh">'</span>
</code></pre>
</div>
<p>You can also see that Ditto has one move, named "transform":<br>
</p>
<div class="highlight js-code-highlight">
<pre class="highlight python"><code><span class="o">>>></span> <span class="n">ditto</span><span class="p">[</span><span class="sh">'</span><span class="s">moves</span><span class="sh">'</span><span class="p">][</span><span class="mi">0</span><span class="p">][</span><span class="sh">'</span><span class="s">move</span><span class="sh">'</span><span class="p">][</span><span class="sh">'</span><span class="s">name</span><span class="sh">'</span><span class="p">]</span>
<span class="sh">'</span><span class="s">transform</span><span class="sh">'</span>
</code></pre>
</div>
<p>You can also get the endpoint for learning more about the transform move:<br>
</p>
<div class="highlight js-code-highlight">
<pre class="highlight python"><code><span class="o">>>></span> <span class="n">ditto</span><span class="p">[</span><span class="sh">'</span><span class="s">moves</span><span class="sh">'</span><span class="p">][</span><span class="mi">0</span><span class="p">][</span><span class="sh">'</span><span class="s">move</span><span class="sh">'</span><span class="p">][</span><span class="sh">'</span><span class="s">url</span><span class="sh">'</span><span class="p">]</span>
<span class="sh">'</span><span class="s">https://pokeapi.co/api/v2/move/144/</span><span class="sh">'</span>
</code></pre>
</div>
<p>You can then call the <code>/move/:move_id</code> endpoint using <code>requests</code> to get its info. That will lead to another response which links to yet further endpoints. <strong>There are so many endpoints!</strong> They're all under the same PokéAPI umbrella, but there's not much structure in the code which reflects this fact. If some of these endpoints return a single string value instead of JSON, the code to interact with the data will need to change as well. Is there a better way to keep this all straight?</p>
<p>At scale, it's important to understand all the APIs and endpoints your code is calling, in part so that you don't spend effort creating code that duplicates existing functionality. It also makes it easier to survey usage to understand if you've migrated fully away from a deprecated API or endpoint. To do this you need a single source of truth for an API and its endpoints, and ideally a homogeneous way of interacting with them.</p>
<p><a href="proxy.php?url=https://github.com/ithaka/apiron" rel="noopener noreferrer"><code>apiron</code></a> is a Python package that addresses these desires by providing a declarative approach that produces SDK-like interaction for you to use. Through introspection of your declared configuration, <code>apiron</code> allows you to start talking to an API quickly and leads you toward a centralized configuration for all of your API dependencies. Let's look at how you might set up the PokéAPI using <code>apiron</code>.</p>
<div class="ltag-github-readme-tag">
<div class="readme-overview">
<h2>
<img src="proxy.php?url=https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo">
<a href="proxy.php?url=https://github.com/ithaka" rel="noopener noreferrer">
ithaka
</a> / <a href="proxy.php?url=https://github.com/ithaka/apiron" rel="noopener noreferrer">
apiron
</a>
</h2>
<h3>
🍳 apiron is a Python package that helps you cook a tasty client for RESTful APIs. Just don't wash it with SOAP.
</h3>
</div>
<div class="ltag-github-body">
<div id="readme" class="md">
<div class="markdown-heading">
<h1 class="heading-element">apiron</h1>
</div>
<p><a href="proxy.php?url=https://pypi.org/project/apiron/#history" rel="nofollow noopener noreferrer"><img src="proxy.php?url=https://camo.githubusercontent.com/e2fe2e260d69fcb914376b446bbf6684e6283d37e3ac35413de291ea49f240a5/68747470733a2f2f62616467652e667572792e696f2f70792f617069726f6e2e737667" alt="PyPI version"></a>
<a href="proxy.php?url=https://pypi.org/project/apiron/" rel="nofollow noopener noreferrer"><img src="proxy.php?url=https://camo.githubusercontent.com/c24935526358ab1673aac2875262742698fd42f94d184962936d15cb7074106d/68747470733a2f2f696d672e736869656c64732e696f2f707970692f707976657273696f6e732f617069726f6e2e737667" alt="Supported Python versions"></a>
<a href="proxy.php?url=https://github.com/ithaka/apiron/actions" rel="noopener noreferrer"><img src="proxy.php?url=https://github.com/github/docs/actions/workflows/main.yml/badge.svg" alt="Build status"></a>
<a href="proxy.php?url=https://apiron.readthedocs.io" rel="nofollow noopener noreferrer"><img src="proxy.php?url=https://camo.githubusercontent.com/b2350c5ae9c522ad21224eed2136d369e061cd6d3fecd6d298f97bd40b607174/68747470733a2f2f72656164746865646f63732e6f72672f70726f6a656374732f617069726f6e2f62616467652f3f76657273696f6e3d6c6174657374" alt="Documentation Status"></a>
<a href="proxy.php?url=https://github.com/ithaka/apironcode-of-conduct.md" rel="noopener noreferrer"><img src="proxy.php?url=https://camo.githubusercontent.com/0008c547b6bdacad0508eb56adccd5019eda8d657c409a228965ec18fd8fa914/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f436f6e7472696275746f72253230436f76656e616e742d76312e3425323061646f707465642d6666363962342e737667" alt="Contributor Covenant"></a></p>
<p><code>apiron</code> helps you cook a tasty client for RESTful APIs. Just don't wash it with SOAP.</p>
<p><a rel="noopener noreferrer" href="proxy.php?url=https://github.com/ithaka/apiron/raw/dev/docs/_static/cast-iron-skillet.png"><img src="proxy.php?url=https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fithaka%2Fapiron%2Fraw%2Fdev%2Fdocs%2F_static%2Fcast-iron-skillet.png" alt="Pie in a cast iron skillet" width="200"></a></p>
<p>Gathering data from multiple services has become a ubiquitous task for web application developers
The complexity can grow quickly
calling an API endpoint with multiple parameter sets,
calling multiple API endpoints,
calling multiple endpoints in multiple APIs.
While the business logic can get hairy,
the code to interact with those APIs doesn't have to.</p>
<p><code>apiron</code> provides declarative, structured configuration of services and endpoints
with a unified interface for interacting with them.</p>
<div class="markdown-heading">
<h2 class="heading-element">Defining a service</h2>
</div>
<p>A service definition requires a domain
and one or more endpoints with which to interact:</p>
<div class="highlight highlight-source-python notranslate position-relative overflow-auto js-code-highlight">
<pre><span class="pl-k">from</span> <span class="pl-s1">apiron</span> <span class="pl-k">import</span> <span class="pl-v">JsonEndpoint</span>, <span class="pl-v">Service</span>
<span class="pl-k">class</span> <span class="pl-v">GitHub</span>(<span class="pl-v">Service</span>):
<span class="pl-s1">domain</span> <span class="pl-c1">=</span> <span class="pl-s">'https://api.github.com'</span>
<span class="pl-s1">user</span> <span class="pl-c1">=</span> <span class="pl-v">JsonEndpoint</span>(<span class="pl-s1">path</span><span class="pl-c1">=</span><span class="pl-s">'/users/{username}'</span>)
<span class="pl-s1">repo</span> <span class="pl-c1">=</span> <span class="pl-v">JsonEndpoint</span>(<span class="pl-s1">path</span><span class="pl-c1">=</span><span class="pl-s">'/repos/{org}/{repo}'</span>)</pre>
</div>
<div class="markdown-heading">
<h2 class="heading-element">Interacting with a service</h2>
</div>
<p>Once your service definition is in place, you can interact…</p>
</div>
</div>
<div class="gh-btn-container"><a class="gh-btn" href="proxy.php?url=https://github.com/ithaka/apiron" rel="noopener noreferrer">View on GitHub</a></div>
</div>
<p>With <code>apiron</code> you can define a <code>Service</code>, which has a <code>domain</code> and a collection of <code>Endpoint</code>s. The Pokémon and move endpoints happen to return JSON, so using a <code>JsonEndpoint</code> will end up returning the response as a dictionary by default. Each endpoint has placeholders that can be filled in dynamically:<br>
</p>
<div class="highlight js-code-highlight">
<pre class="highlight python"><code><span class="kn">from</span> <span class="n">apiron</span> <span class="kn">import</span> <span class="n">Service</span><span class="p">,</span> <span class="n">JsonEndpoint</span>
<span class="k">class</span> <span class="nc">PokeAPI</span><span class="p">(</span><span class="n">Service</span><span class="p">):</span>
<span class="n">domain</span> <span class="o">=</span> <span class="sh">'</span><span class="s">https://pokeapi.co</span><span class="sh">'</span>
<span class="n">pokemon</span> <span class="o">=</span> <span class="nc">JsonEndpoint</span><span class="p">(</span><span class="n">path</span><span class="o">=</span><span class="sh">'</span><span class="s">/api/v2/pokemon/{pokemon}</span><span class="sh">'</span><span class="p">)</span>
<span class="n">move</span> <span class="o">=</span> <span class="nc">JsonEndpoint</span><span class="p">(</span><span class="n">path</span><span class="o">=</span><span class="sh">'</span><span class="s">/api/v2/move/{move_id}</span><span class="sh">'</span><span class="p">)</span>
</code></pre>
</div>
<p>This is all fine, but how do you interact with it? <code>apiron</code> provides SDK-like interaction for calling your configured service. That is, <code>apiron</code> tries to feel like something purpose-built for the API you need to use while being flexible enough to do this for any number of APIs. Here's how to make the call to get Ditto's information and information about the transform move:<br>
</p>
<div class="highlight js-code-highlight">
<pre class="highlight python"><code><span class="n">ditto</span> <span class="o">=</span> <span class="n">PokeAPI</span><span class="p">.</span><span class="nf">pokemon</span><span class="p">(</span><span class="n">path_kwargs</span><span class="o">=</span><span class="p">{</span><span class="sh">'</span><span class="s">pokemon</span><span class="sh">'</span><span class="p">:</span> <span class="sh">'</span><span class="s">ditto</span><span class="sh">'</span><span class="p">})</span>
<span class="n">transform</span> <span class="o">=</span> <span class="n">PokeAPI</span><span class="p">.</span><span class="nf">move</span><span class="p">(</span><span class="n">path_kwargs</span><span class="o">=</span><span class="p">{</span><span class="sh">'</span><span class="s">move_id</span><span class="sh">'</span><span class="p">:</span> <span class="mi">144</span><span class="p">})</span>
</code></pre>
</div>
<p>From this code, it's easier to see that you're calling the PokéAPI and using the Pokémon and move endpoints. It's also easier to see what data is being plugged into the calls. If you're using an IDE, you can also jump to those endpoint definitions to see that they return JSON, so you get an idea of what to expect when using the responses. As you look through the <code>PokeAPI</code> class, you can also see which endpoints are already implemented at a glance, and quickly add any that aren't available yet if you know the right path and returned data type.</p>
<p>If the PokéAPI moves to a new domain, or if an endpoint's path changes, there's one clear place to react to that change. You can also call an endpoint with different headers, cookies, and more without having to duplicate much boilerplate. The readability and inspectability of this approach have improved my experience dealing with numerous services with numerous endpoints. In addition to helping users develop clients for APIs they want to use, this package can potentially reduce the effort needed for API providers to give their consumers a Python SDK. I really hope you'll try it out and tell me all the ways you can think of to break it!</p>
pythonwebdevopensourceshowdevTruly taking time off: A checklistDane HillardSun, 03 Mar 2019 18:48:08 +0000
https://dev.to/easyaspython/truly-taking-time-off-a-checklist-14ei
https://dev.to/easyaspython/truly-taking-time-off-a-checklist-14ei
<p>This post originally appeared on <a href="proxy.php?url=https://dane.engineering/post/truly-taking-time-off-a-checklist">dane.engineering</a></p>
<p>I recently returned from nearly two weeks off from my typical day-to-day work. Part of this time was to attend a conference (<a href="proxy.php?url=http://pycaribbean.com">PyCaribbean</a> was amazing!) and the rest was a legitimate vacation. In the past I've had trouble really disconnecting and enjoying my time away, so this time I took deliberate measures to make sure I didn't fall into the same trap again.</p>
<h2>
Empty your brain
</h2>
<p>My time away coincided with a pretty busy time for my team, leading up to a deadline. This can be one of the more difficult times to step away. Paramount to your team's success and your ease of mind while you're gone is making sure everything you have in your head gets out. Disseminate everything you know or have in progress so that there's shared understanding. Ideally, pair with a coworker leading up to your vacation to transition anything that needs to keep rolling while you're gone.</p>
<p>Make agreements as a team about what's expected of you before you leave so that you can commit to getting those things done. It will not only help the team feel harmonized, but will give you a clear stopping point that will mentally let you switch to vacation mode.</p>
<h2>
Escape from Slack
</h2>
<p>Slack can be far and away the biggest source of distraction any time you're taking some time to yourself. This is especially true if you use it on your mobile devices. If you only do one thing after syncing up with your team, it should be this.</p>
<p>Slack statuses can be set until a specific date and time, or indefinitely. Set a status with an emoji indicating you're away/on vacation/unavailable, indicating when you'll be back if possible.</p>
<p><a href="proxy.php?url=https://res.cloudinary.com/practicaldev/image/fetch/s--BQwfvy4c--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/mh4pj49mdi2z73e0qgs2.png" class="article-body-image-wrapper"><img src="proxy.php?url=https://res.cloudinary.com/practicaldev/image/fetch/s--BQwfvy4c--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/mh4pj49mdi2z73e0qgs2.png" alt="Setting a Slack status"></a></p>
<p>In addition to a status, Slack lets you explicitly set yourself to "Away." This makes it more clear in the interface that you're not around, so it helps manage expectations!</p>
<p>I chose the nuclear option on this trip and signed myself out of Slack completely. In addition to removing the occasional distracting notification, it made me less likely to check Slack on impulse during down time.</p>
<h2>
Escape from email
</h2>
<p>In a similar vein to Slack, email is one of those things a lot of us check "just in case." There is rarely anything compelling in your inbox, at least nothing that can't wait until you're back. Just make sure there's a protocol for true emergencies, like a personal phone number or email. Ideally these are behind some sort of "BREAK IN CASE OF EMERGENCY" glass. Set an auto-reply message similar to your Slack status, but in addition include the names or contact information of people to contact in your stead. This helps folks get their questions and concerns addressed more quickly.</p>
<h2>
Escape from your calendar
</h2>
<p>Calendar reminders are another source of distraction, and one I only realized when I was preparing for this trip. Calendars are also handy for trip planning and organizing, so if there are still recurring work meetings floating around they can cause clutter.</p>
<p>Decline meetings that fall under the time you'll be gone. If they're recurring, make sure you only decline the instance instead of the series! Declining meetings reminds people you won't be attending, which helps them plan their agendas while you're out.</p>
<h2>
Escape from the pager
</h2>
<p>If you're on rotation for your team's application monitoring, make sure that you're not scheduled during the time you're out. Swap with someone if you are (and show your appreciation). If unattended alerts get escalated to everyone, you can consider removing yourself from the escalation policy temporarily as well. I didn't think of that beforehand, so I was still caught by a few escalated alerts.</p>
<h2>
It's about balance
</h2>
<p>I mentioned that in the past I've had a hard time disconnecting. Part of this is because I personally don't mind being mildly available even when I'm out. This is the balance I'm comfortable with. For others, the balance falls elsewhere, further toward some end of the spectrum. The tips above fall at the "unavailable" end of the spectrum, so you can draw on the bits and pieces you need to strike the right balance for yourself. Be healthy and be well!</p>
<p>What are your tips for unplugging and enjoying time away?</p>
vacationtimeoffworklifebalanceIntegrating ButterCMS in your Vue applicationDane HillardTue, 05 Feb 2019 22:52:42 +0000
https://dev.to/easyaspython/integrating-buttercms-in-your-vue-application-2ihb
https://dev.to/easyaspython/integrating-buttercms-in-your-vue-application-2ihb<p>This post originally appeared on <a href="proxy.php?url=https://dane.engineering/post/buttercms-in-vue/">dane.engineering</a>.</p>
<p>Although sites like Medium provide audience reach and a consistent experience, the list of reasons I'm no longer happy with it keeps growing. I've done most of my recent writing here on dev.to, which has been a refreshing change!</p>
<p>I decided to take my maiden voyage into headless CMS, and I've chosen <a href="proxy.php?url=https://buttercms.com">ButterCMS</a> for the moment. Its small but focused API was plenty for me to get my blog up and running! Best of all, it's free for non-commercial use.</p>
<p>Right after you sign up with Butter, you'll get an API key and links to an impressive array of framework-specific instructions for how to get set up. I found the instructions for Vue very easy to follow, and was able to go from signing up to retrieving the test blog post within 15 minutes or so! I'm using single-file components (SFCs) in my app, so I started by grabbing a list of blog posts and creating the template together. Butter returns a <code>data</code> property that contains the list of posts, each of which has a title, slug, body, and so on. We can fetch these when the <code>BlogPostList</code> component is created, so that the data becomes available around the time it mounts. I also put in a loading state for the rare occasion where the API response takes a noticeable amount of time:<br>
</p>
<div class="highlight js-code-highlight">
<pre class="highlight html"><code><span class="nt"><template></span>
<span class="nt"><main></span>
<span class="nt"><h1></span>Posts<span class="nt"></h1></span>
<span class="nt"><span</span> <span class="na">v-if=</span><span class="s">"loading"</span><span class="nt">></span>Loading...<span class="nt"></span></span>
<span class="nt"><ul</span> <span class="na">v-else-if=</span><span class="s">"posts.data.length"</span><span class="nt">></span>
<span class="nt"><li</span> <span class="na">v-for=</span><span class="s">"post in posts.data"</span> <span class="na">:key=</span><span class="s">"post.slug"</span><span class="nt">></span>
<span class="nt"><router-link</span> <span class="na">:to=</span><span class="s">"{ name: 'post', params: { slug: post.slug } }"</span><span class="nt">></span>
{{ post.title }}
<span class="nt"></router-link></span>
<span class="nt"></li></span>
<span class="nt"></ul></span>
<span class="nt"></main></span>
<span class="nt"></template></span>
<span class="nt"><script></span>
<span class="k">import</span> <span class="nx">Butter</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">buttercms</span><span class="dl">'</span>
<span class="kd">const</span> <span class="nx">butter</span> <span class="o">=</span> <span class="nx">Butter</span><span class="p">(</span><span class="dl">'</span><span class="s1">YOUR API KEY</span><span class="dl">'</span><span class="p">)</span>
<span class="k">export</span> <span class="k">default</span> <span class="p">{</span>
<span class="na">name</span><span class="p">:</span> <span class="dl">'</span><span class="s1">BlogPostList</span><span class="dl">'</span><span class="p">,</span>
<span class="nx">data</span> <span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="p">{</span>
<span class="na">loading</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
<span class="na">posts</span><span class="p">:</span> <span class="p">{},</span>
<span class="p">}</span>
<span class="p">},</span>
<span class="na">methods</span><span class="p">:</span> <span class="p">{</span>
<span class="nx">fetchPosts</span> <span class="p">()</span> <span class="p">{</span>
<span class="nx">butter</span><span class="p">.</span><span class="nx">post</span><span class="p">.</span><span class="nx">list</span><span class="p">({</span> <span class="na">page</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="na">pageSize</span><span class="p">:</span> <span class="mi">10</span> <span class="p">})</span>
<span class="p">.</span><span class="nx">then</span><span class="p">((</span><span class="nx">response</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">loading</span> <span class="o">=</span> <span class="kc">false</span>
<span class="k">this</span><span class="p">.</span><span class="nx">posts</span> <span class="o">=</span> <span class="nx">response</span><span class="p">.</span><span class="nx">data</span>
<span class="p">}).</span><span class="k">catch</span><span class="p">((</span><span class="nx">response</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">response</span><span class="p">)</span>
<span class="p">})</span>
<span class="p">},</span>
<span class="p">},</span>
<span class="nx">created</span> <span class="p">()</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">fetchPosts</span><span class="p">()</span>
<span class="p">},</span>
<span class="p">}</span>
<span class="nt"></script></span>
</code></pre>
</div>
<p>The code for displaying a single post is much the same—a <code>data</code> property contains information about the post and we can fetch the post as the component is created:<br>
</p>
<div class="highlight js-code-highlight">
<pre class="highlight html"><code><span class="nt"><template></span>
<span class="nt"><main></span>
<span class="nt"><article</span> <span class="na">v-if=</span><span class="s">"post.data"</span><span class="nt">></span>
<span class="nt"><h1></span>{{ post.data.title }}<span class="nt"></h1></span>
<span class="nt"><span</span> <span class="na">class=</span><span class="s">"metadata"</span><span class="nt">></span>{{ publishedDate }}<span class="nt"></span></span>
<span class="nt"><div</span> <span class="na">v-html=</span><span class="s">"post.data.body"</span> <span class="nt">/></span>
<span class="nt"></article></span>
<span class="nt"></main></span>
<span class="nt"></template></span>
<span class="nt"><script></span>
<span class="k">import</span> <span class="nx">Butter</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">buttercms</span><span class="dl">'</span>
<span class="kd">const</span> <span class="nx">butter</span> <span class="o">=</span> <span class="nx">Butter</span><span class="p">(</span><span class="dl">'</span><span class="s1">YOUR API KEY</span><span class="dl">'</span><span class="p">)</span>
<span class="k">export</span> <span class="k">default</span> <span class="p">{</span>
<span class="na">name</span><span class="p">:</span> <span class="dl">'</span><span class="s1">BlogPost</span><span class="dl">'</span><span class="p">,</span>
<span class="nx">data</span> <span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="p">{</span>
<span class="na">loading</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
<span class="na">post</span><span class="p">:</span> <span class="p">{},</span>
<span class="p">}</span>
<span class="p">},</span>
<span class="na">methods</span><span class="p">:</span> <span class="p">{</span>
<span class="nx">fetchPost</span> <span class="p">()</span> <span class="p">{</span>
<span class="nx">butter</span><span class="p">.</span><span class="nx">post</span><span class="p">.</span><span class="nx">retrieve</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">$route</span><span class="p">.</span><span class="nx">params</span><span class="p">.</span><span class="nx">slug</span><span class="p">)</span>
<span class="p">.</span><span class="nx">then</span><span class="p">((</span><span class="nx">response</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">loading</span> <span class="o">=</span> <span class="kc">false</span>
<span class="k">this</span><span class="p">.</span><span class="nx">post</span> <span class="o">=</span> <span class="nx">response</span><span class="p">.</span><span class="nx">data</span>
<span class="p">}).</span><span class="k">catch</span><span class="p">((</span><span class="nx">response</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">response</span><span class="p">)</span>
<span class="p">})</span>
<span class="p">},</span>
<span class="p">},</span>
<span class="nx">created</span> <span class="p">()</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">fetchPost</span><span class="p">()</span>
<span class="p">},</span>
<span class="p">}</span>
<span class="nt"></script></span>
</code></pre>
</div>
<p>This is about all you need in order to start fetching a list of posts and rendering each post! The rest of the work is in dropping all the SEO into place (I use <a href="proxy.php?url=https://github.com/nuxt/vue-meta"><code>vue-meta</code></a> for this) and styling the content as you wish. One additional thing I did to make using Butter easier throughout my Vue app was adding it to the Vue prototype:<br>
</p>
<div class="highlight js-code-highlight">
<pre class="highlight javascript"><code><span class="k">import</span> <span class="nx">Butter</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">buttercms</span><span class="dl">'</span>
<span class="kd">const</span> <span class="nx">butter</span> <span class="o">=</span> <span class="nx">Butter</span><span class="p">(</span><span class="dl">'</span><span class="s1">YOUR API KEY</span><span class="dl">'</span><span class="p">)</span>
<span class="nx">Vue</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">$butter</span> <span class="o">=</span> <span class="nx">butter</span>
</code></pre>
</div>
<p>Then I can just type <code>this.$butter</code> in a component's methods instead of initializing the API client each time.</p>
<p>The features I like about butter so far are:</p>
<ul>
<li>The easy-to-use API and corresponding JavaScript client</li>
<li>The small amount of time it took to get started</li>
<li>The image API they provide (through Filestack)</li>
</ul>
<p>The features that could use some work are:</p>
<ul>
<li>Adding code blocks to your posts: it's way nicer than Medium, but not as good an experience in the WYSIWYG as other more basic things</li>
<li>Terminology: this isn't unique to Butter; a CMS can be complex so it's difficult to distinguish all the concepts you can configure, create, and customize</li>
</ul>
vueheadlesscmswebdevI'm writing Practices of the Python ProDane HillardThu, 31 Jan 2019 01:48:25 +0000
https://dev.to/easyaspython/im-writing-code-like-a-pro-software-development-in-python-39ji
https://dev.to/easyaspython/im-writing-code-like-a-pro-software-development-in-python-39ji<p>I'm doing a thing.</p>
<p><a href="proxy.php?url=https://thepythonpro.com" rel="noopener noreferrer"><img src="proxy.php?url=https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fjse6n7cvh22o8f1172wh.png" alt="Practices of the Python Pro banner"></a></p>
<p>I'm pretty stoked to announce that the book I've been working on for the past few months, <a href="proxy.php?url=https://thepythonpro.com" rel="noopener noreferrer"><em>Practices of the Python Pro</em></a>, is now available for early access! <strong>The first chapter is available for free</strong>, and the first three chapters are currently available if you choose to order the book! I'm able to offer everyone 50% off until February 7th by using the code <code>mlhillard</code> at checkout (please let me know if you have any issues at all).</p>
<p>I'm brand new to writing books, so even though I'm teaching others it's been a huge learning process for me as well. I've been writing blog posts and documentation for a while now, but taking someone on a journey through any concept from start to finish is an undertaking! It's certainly the most I've had to think about grammar for about a decade 😄</p>
<p>The review and editing process has been paramount in helping me teach better. Turning around the edits on a chapter is often the most difficult part of writing, but it's resulted in what I hope is a great teaching pace. My goal with this book is to make concepts used every day by experienced software developers accessible and exciting to those just entering the field, all in the context of Python. I'm covering things like namespacing, separation of concerns, abstraction, testing, and design patterns. Python's popularity means people come into it from all angles, so the book will be a source for anyone looking to take the next step in their software journey!</p>
<p>If you find you're interested enough in the book to <a href="proxy.php?url=https://thepythonpro.com" rel="noopener noreferrer">buy it in early access</a> I'm hoping you will help me help others by providing detailed feedback about anything that's confusing, misleading, or missing. The DEV community has been one of the most supportive, inclusive, inquisitive communities I've ever been a part of and I want to produce something that embodies these values!</p>
<p>I'm sure that I will have plenty more to write about the book writing experience as things progress. <strong>I'd love to answer any questions you have; whether about the book, the process, or anything else!</strong></p>
pythondiscussbeginnersshowdevBe more productive with shell aliasesDane HillardSat, 26 Jan 2019 17:02:22 +0000
https://dev.to/easyaspython/be-more-productive-with-shell-aliases-2b16
https://dev.to/easyaspython/be-more-productive-with-shell-aliases-2b16<p>The most valuable time as a software developer is usually time spent collaborating and thinking. To get as much of that time as possible, reduce the time you spend typing things with your mortal fingers!</p>
<h2>
Git
</h2>
<p>Git is a ubiquitous version control system, and becomes more important as many developers begin collaborating on the same code base. If your organization uses Agile development practices, you'll also be creating many branches and updating <code>master</code> a lot.</p>
<p>I use <a href="proxy.php?url=https://github.com/robbyrussell/oh-my-zsh"><code>oh-my-zsh</code></a>'s <a href="proxy.php?url=https://github.com/robbyrussell/oh-my-zsh/blob/master/plugins/git/git.plugin.zsh">Git plugin</a> which provides an amazing variety of aliases for common (and not-so-common) Git subcommands. A common workflow in git is to:</p>
<ol>
<li>Check out the <code>master</code> branch</li>
<li>Update your local <code>master</code> branch with the latest changes from the remote</li>
<li>Create a new branch for your changes</li>
<li>Commit your changes with a <a href="proxy.php?url=https://chris.beams.io/posts/git-commit/">good commit message</a>
</li>
<li>Push your branch to the remote</li>
</ol>
<p>Without any aliases, this workflow looks like this on the command line:<br>
</p>
<div class="highlight js-code-highlight">
<pre class="highlight shell"><code><span class="nv">$ </span>git checkout master
<span class="nv">$ </span>git pull origin master
<span class="nv">$ </span>git checkout <span class="nt">-b</span> my-feature
<span class="c"># Make changes...</span>
<span class="nv">$ </span>git add <span class="nb">.</span>
<span class="nv">$ </span>git commit <span class="nt">-am</span> <span class="s1">'Add my feature'</span>
<span class="nv">$ </span>git push origin my-feature
</code></pre>
</div>
<p>This is fine, but sometimes I forget that pesky <code>-b</code> flag when creating a branch and also get annoyed that I need to state which branch I'm pushing when it's almost always the current one.</p>
<p>With the Git plugin aliases, this workflow takes a lot less work:<br>
</p>
<div class="highlight js-code-highlight">
<pre class="highlight shell"><code><span class="nv">$ </span>gcm
<span class="nv">$ </span>gl origin master
<span class="nv">$ </span>gcb my-feature
<span class="c"># Make changes...</span>
<span class="nv">$ </span>ga <span class="nb">.</span>
<span class="nv">$ </span>gcam <span class="s1">'Add my feature'</span>
<span class="nv">$ </span>ggpush <span class="c"># this takes advantage of oh-my-zsh's git_current_branch</span>
</code></pre>
</div>
<p>Sometimes you need to stash your changes while looking at another branch or doing more complex things in Git:<br>
</p>
<div class="highlight js-code-highlight">
<pre class="highlight shell"><code><span class="nv">$ </span>git stash
<span class="c"># Do some stuff and come back</span>
<span class="nv">$ </span>git stash pop
</code></pre>
</div>
<p>But these aliases come in handy:<br>
</p>
<div class="highlight js-code-highlight">
<pre class="highlight shell"><code><span class="nv">$ </span>gsta
<span class="c"># Do some stuff and come back</span>
<span class="nv">$ </span>gstp
</code></pre>
</div>
<p>There are a lot of handy aliases, including plenty with the various flags you might use for a particular Git subcommand:<br>
</p>
<div class="highlight js-code-highlight">
<pre class="highlight shell"><code><span class="nv">$ </span>gd <span class="c"># git diff</span>
<span class="nv">$ </span>gdca <span class="c"># git diff --cached</span>
<span class="nv">$ </span>gds <span class="c"># git diff --staged</span>
</code></pre>
</div>
<p>I love using these, and the shorthand for them is generally intuitable kind of like Vim. I've added a few of my own for command+flag combinations I use commonly, like:<br>
</p>
<div class="highlight js-code-highlight">
<pre class="highlight shell"><code><span class="nv">$ </span>goop <span class="c"># "git oops": git reset --soft HEAD~1</span>
<span class="nv">$ </span>gun <span class="c"># "git unstage": git reset HEAD --</span>
</code></pre>
</div>
<p>I also checkout and update master then prune cleaned up remote branches so often that I made a single command, <a href="proxy.php?url=https://github.com/daneah/dotfiles/blob/4b0b691013a8dcf5f3ef6e8f44157187ba6e14d2/source/functions.zsh#L10"><code>upr</code></a> (for "update repo"), that does this all for me.</p>
<h2>
Python
</h2>
<p>I work in Python and Django most days. The ease and length of my aliases in this arena reflect that; the more frequently I run a command, the shorter its alias:<br>
</p>
<div class="highlight js-code-highlight">
<pre class="highlight shell"><code><span class="nv">$ </span>python manage.py runserver <span class="c"># Nope</span>
<span class="nv">$ </span>./manage.py runserver <span class="c"># Nope</span>
<span class="nv">$ </span>run <span class="c"># There we go</span>
</code></pre>
</div>
<div class="highlight js-code-highlight">
<pre class="highlight shell"><code><span class="nv">$ </span>pipenv shell <span class="c"># Nope</span>
<span class="nv">$ </span>psh <span class="c"># Yep</span>
</code></pre>
</div>
<div class="highlight js-code-highlight">
<pre class="highlight shell"><code><span class="nv">$ </span>pip <span class="nb">install</span> <span class="nt">-r</span> requirements.txt <span class="c"># Hmmm</span>
<span class="nv">$ </span>prq <span class="c"># Indeed!</span>
</code></pre>
</div>
<h2>
Odds and ends
</h2>
<p>There are a few general command utilities that I use often enough to slim them down too. The one I use the absolute most is <code>named</code>, which expands to <code>find . -name</code>. Then I just ask for files <code>named "*.py"</code>! I also default <code>grep</code> to using colors and line numbers by aliasing it to <code>grep -n --color=auto</code>. I can use the bare <code>grep</code> by typing <code>\grep</code> on the rare occasions where it's needed.</p>
<p>You can see all of my aliases <a href="proxy.php?url=https://github.com/daneah/dotfiles/tree/master/source/aliases.zsh">here</a>.</p>
<h2>
Conclusion
</h2>
<p>The point of all this is that you should spend time <em>thinking</em> about what you want to do and as little time as possible <em>doing</em> what you want to do when using the command line or writing code. I'm able to reincorporate all the time I save into more thinking—usually about all the bugs I've introduced 😉</p>
discussproductivityshell