<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0"><channel><description>A Meetup, Inc “behind the scenes” look at what our engineering team is working on.</description><title>Making Meetup</title><generator>Tumblr (3.0; @makingmeetup)</generator><link>http://making.meetup.com/</link><item><title>Meetup Engineering’s Rich Hsieh on Culture Fit</title><description>&lt;p&gt;This week, Meetup&amp;rsquo;s own Rich Hsieh sat down with the folks at Devpost where they talked about culture fit. What exactly is it?  And how is it valued at a company like Meetup?  Check out the discussion here:&lt;/p&gt;

&lt;iframe width="560" height="315" src="https://www.youtube.com/embed/YS1SrwmPAEg" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;

&lt;p&gt;And then check out our awesome Devpost page where you can read more about what it&amp;rsquo;s like to work at Meetup, and what the interview process here is like.  And you can check out all of our openings!&lt;/p&gt;

&lt;p&gt;&lt;a href="http://devpost.com/teams/meetup" target="_blank"&gt;http://devpost.com/teams/meetup&lt;/a&gt;&lt;/p&gt;</description><link>http://making.meetup.com/post/143164276182</link><guid>http://making.meetup.com/post/143164276182</guid><pubDate>Thu, 21 Apr 2016 09:50:49 -0400</pubDate><category>devpost</category><category>hiring</category><category>meetup</category><category>culture fit</category><category>rich hsieh</category><dc:creator>jakemeetup</dc:creator></item><item><title>Making Meetup’s Apps @ Mobile Week NYC</title><description>&lt;p&gt;Last week I was fortunate enough to give the keynote at &lt;a href="http://mobileweek.co/" target="_blank"&gt;Mobile Week NYC&lt;/a&gt;.  It was entitled “&lt;i&gt;It’s Time! What to do When Rebuilding Your App?&lt;/i&gt;” and is a case study in how Meetup made the exciting (and difficult) decision to rebuild and redesign our apps. &lt;/p&gt;&lt;figure data-orig-width="1920" data-orig-height="1080" class="tmblr-full"&gt;&lt;img src="http://41.media.tumblr.com/8f773042a07c3960b8c57f65e1cda1b8/tumblr_inline_o5ukhw1YWc1u0rkvc_540.png" alt="image" data-orig-width="1920" data-orig-height="1080"/&gt;&lt;/figure&gt;&lt;p&gt;The keynote meant to answer the big question: “&lt;i&gt;Once teams are spun up and start working on a project like this, then what?&lt;/i&gt;”  Here are a few of the key Android and iOS engineering topics I covered in my talk.&lt;/p&gt;&lt;p&gt;&lt;b&gt;iOS&lt;/b&gt;&lt;/p&gt;&lt;p&gt;One of the biggest decisions we had to make around rebuilding our apps was with our iOS app.  Some background on our iOS app is that it was originally built in a 3 month extended hackathon type project back in 2009 by two engineers that did an awesome job getting an MVP shipped. The problem was, it was never re-written over the course of 6 years, accumulating a lot of technical debt. We knew the code wasn’t of the quality we wanted and our test coverage was way too low for our current standards.  &lt;/p&gt;&lt;p&gt;Also, much of the institutional knowledge from those who had built the app over 6 years was gone, so we hired — in our opinion — the best mobile engineering shop in NYC — &lt;a href="http://lickability.com/" target="_blank"&gt;Lickability&lt;/a&gt; — to do an in-depth analysis of our codebase. Their output was a spreadsheet of every class in the app (almost 500 classes!), a recommendation as to whether or not the class was salvageable, and metrics around the test coverage.&lt;/p&gt;&lt;figure data-orig-width="1920" data-orig-height="1080" class="tmblr-full"&gt;&lt;img src="http://41.media.tumblr.com/4363f74fa49c348b785829b865d5a8b2/tumblr_inline_o5uko6UfS51u0rkvc_540.png" alt="image" data-orig-width="1920" data-orig-height="1080"/&gt;&lt;/figure&gt;&lt;p&gt;Following such a detailed assessment, we had the data to know that we needed to re-write almost our entire model code. Naturally, in a redesign you have to rewrite the view/UI code as well, so it was a clear decision for us that in order to futurize Meetup, investing more time in a new codebase that met our quality and testing standards was the right decision for us.  Futurize is one of our company values and means: “ We like shipping, but we like investing to ship faster in the future even more.”&lt;/p&gt;&lt;figure class="tmblr-full" data-orig-height="576" data-orig-width="1024"&gt;&lt;img src="http://41.media.tumblr.com/7d398fd183d4e49fb236184e914d4459/tumblr_inline_o5w0etBgAU1u0rkvc_540.jpg" data-orig-height="576" data-orig-width="1024"/&gt;&lt;/figure&gt;&lt;p&gt;After making that decision in 2015, it seemed obvious to us that if we’re going to rebuild our app, &lt;a href="https://swift.org/" target="_blank"&gt;Swift&lt;/a&gt; was the only way to go. Among many other reasons we love Swift, we especially love that it is statically typed so is type safe just like the rest of our Scala/JVM stack.&lt;/p&gt;&lt;p&gt;Once the decision was made to rebuild our iOS app from scratch, the next key decision we had to make was Swift 1 or 2, because Swift 2 was still in beta. We decided on Swift 2 because it both compiles and runs faster, is a great boost to our team’s morale to be able to start off working with and contributing to a the latest open source iOS language, and we saw it as a great way to attract people who wanted to make Meetup now using the future of iOS.&lt;/p&gt;&lt;figure data-orig-width="1920" data-orig-height="1080" class="tmblr-full"&gt;&lt;img src="http://41.media.tumblr.com/150a858bfb5a98c4bb526907391dfd65/tumblr_inline_o5ukl76Sjh1u0rkvc_540.png" alt="image" data-orig-width="1920" data-orig-height="1080"/&gt;&lt;/figure&gt;&lt;p&gt;The second decision we made was supporting iOS 9 and above for minimum version support in the new app. We had a lot of data that showed how quickly our member base would upgrade to iOS 9 and how few members would be left on iOS 8. We didn’t like that we’d leave any member behind. But, not only did the data support it, but our engineering assessment was that building for iOS 9 and above would significantly reduce the amount of code we’d need to write — freeing us up to build and ship other features faster.  Specifically, supporting iOS 9 allowed us to use the new &lt;a href="https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIStackView_Class_Reference/" target="_blank"&gt;UIStackView&lt;/a&gt; class, providing an easy interface for laying out a collection of views and leveraging Auto Layout for creating UIs that adapt to the device’s screen size and orientation.&lt;/p&gt;&lt;p&gt;&lt;b&gt;Android&lt;/b&gt;&lt;/p&gt;&lt;p&gt;The decision making process around building our Android app was very different. The foundation of our Android app was much better, the code quality and test coverage was much higher, and the lead engineer working on the app had already been working on it for several years.  &lt;/p&gt;&lt;p&gt;We had the institutional knowledge in house to do an assessment of the codebase and determined that we’d have to refactor and re-write significant things, but that there were parts that would serve us well in the future.  During our assessment we identified key areas we needed to address and this is how we handled them.&lt;/p&gt;&lt;p&gt;The biggest change we made was to go all in using &lt;a href="https://github.com/ReactiveX/RxJava" target="_blank"&gt;RxJava&lt;/a&gt; for reactive programming in Android. It really helps us get concurrency under control with little effort because it abstracts everything to far fewer lines of code. This was a big deal for us because we’re loading so much data from the network using multiple API calls and our API has to support many different clients at once (Android, iOS app, Apple Watch, Web). So, it’s hard for the API to always return data to a client app in a single call that’s exactly the way each client app needs it every time.&lt;/p&gt;&lt;figure data-orig-width="1920" data-orig-height="1080" class="tmblr-full"&gt;&lt;img src="http://40.media.tumblr.com/8169bf3d21ce02cf7ee6a4165fcbbcde/tumblr_inline_o5ukpwkEZS1u0rkvc_540.png" alt="image" data-orig-width="1920" data-orig-height="1080"/&gt;&lt;/figure&gt;&lt;p&gt;Our Android team absolutely loves &lt;a href="https://github.com/ReactiveX/RxJava" target="_blank"&gt;RxJava&lt;/a&gt; because they know that they can composite multiple calls on the client (meaning they can easily wait for both to complete, then do something in the app) in just few lines of code. They can do this without adding additional complexity (for example, composing &lt;a href="https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Future.html" target="_blank"&gt;Futures&lt;/a&gt;) or having to manage things like if a user tapped away from the screen.  It’s been the biggest improvement for us in the new app architecture.&lt;/p&gt;&lt;p&gt;We’ve also loved using &lt;a href="http://square.github.io/okhttp/" target="_blank"&gt;OKHTTP&lt;/a&gt; from Square as our HTTP client. It helps us load data from the API into the app faster, save bandwidth usage, and really make our app more responsive &amp;ndash; it just feels and works awesomely fast.  &lt;/p&gt;&lt;p&gt;We use Jackson for data processing and agree with the &lt;a href="https://github.com/FasterXML/jackson" target="_blank"&gt;Jackson Project&lt;/a&gt; page that says it’s “the best data processor for Java”. It supports every format you’d want with streaming parsing and data-binding.&lt;/p&gt;&lt;p&gt;Last, but not least, &lt;a href="https://github.com/google/guava" target="_blank"&gt;Guava&lt;/a&gt; is a set of core libraries from Google that really helped us handle a broad range of things like collections, caching, I/O, and has really reduced the amount of code we’ve needed to write ourselves.&lt;/p&gt;&lt;figure class="tmblr-full" data-orig-height="1365" data-orig-width="1024"&gt;&lt;img src="http://40.media.tumblr.com/8e4e319effec3238dfdcecc2fb587db7/tumblr_inline_o5w0cuP59P1u0rkvc_540.jpg" data-orig-height="1365" data-orig-width="1024"/&gt;&lt;/figure&gt;&lt;p&gt;&lt;b&gt;Thanks!&lt;/b&gt;&lt;/p&gt;&lt;p&gt;Mobile Week NYC was a great experience and there were a lot of interesting talks there. Thanks to the conference organizers for inviting me to speak and to everyone who attended and participated!  I learned a lot and met many passionate, smart people doing great things in mobile. &lt;/p&gt;</description><link>http://making.meetup.com/post/143024807327</link><guid>http://making.meetup.com/post/143024807327</guid><pubDate>Mon, 18 Apr 2016 16:57:01 -0400</pubDate><dc:creator>yvette-meetup</dc:creator></item><item><title>Diversity and inclusion on Meetup’s engineering team</title><description>&lt;p&gt;Our &lt;a href="http://devpost.com/teams/meetup" target="_blank"&gt;Devpost&lt;/a&gt; site contains bios and fun facts about some of our team members, myself included. We’ve also shared &lt;a href="http://devpost.com/teams/meetup#diversity" target="_blank"&gt;data on the ethnicity and gender background for some of our employees&lt;/a&gt;, broken down by the company and our engineering team.&lt;/p&gt;

&lt;p&gt;There are many factors which go into building an inclusive culture at Meetup and this data represents only one part of the picture. A fuller portrait would include people such as our CTO, Yvette Pasqua, and other women in leadership roles. What this data does is provide a snapshot of team members who self-identify within the parameters of federally required EEO-1 reporting categories and have chosen to share this information. I care deeply about inclusion both within and beyond this. For example, this data doesn’t capture gender information aside from “male” and “female” - a topic which I &lt;a href="https://us.pycon.org/2016/schedule/presentation/2023/" target="_blank"&gt;will be speaking about at PyCon this June&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I think that sharing diversity data is a critical first step towards transparency and accountability. I’m proud to work to help create a company whose makeup reflects the diversity of our Meetup members, and to help create an industry whose makeup reflects the diversity of our world.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Anne DeCusatis is a Core Engineer on Meetup’s Retention team.&lt;/em&gt;&lt;/p&gt;</description><link>http://making.meetup.com/post/142298670108</link><guid>http://making.meetup.com/post/142298670108</guid><pubDate>Tue, 05 Apr 2016 12:00:16 -0400</pubDate><category>engineering</category><category>meetup</category><category>diversity</category><category>inclusion</category><category>tech</category><dc:creator>anne-meetup</dc:creator></item><item><title>NE Scala 2016</title><description>&lt;p&gt;Towards the end of the first week of March, on March 4th and March 5th, Meetup put together a small team of engineers who volunteered to attend the &lt;strong&gt;6th&lt;/strong&gt; annual &lt;a href="http://www.nescala.org/" target="_blank"&gt;&lt;em&gt;Northeast Scala Symposium&lt;/em&gt;&lt;/a&gt; in Philadelphia.&lt;/p&gt;

&lt;p&gt;I am an engineer on Meetup’s Member Engagement team, building a system that delivers smart and personalized notifications to members on our platform. I am also a part of Meetup’s Core Engineering team, where we are using Scala a lot in order to build out our platform. Given my work and interest in Scala, I was one of the engineers who participated in the trip, and I’d love to share some of the things I learned during Day 2 of the event!&lt;/p&gt;

&lt;p&gt;This was the first language conference I have ever attended and it was even more exciting to me, given that I only started working in Scala since I joined Meetup, almost 2 years ago, straight out of college!&lt;/p&gt;

&lt;p&gt;&lt;img src="http://33.media.tumblr.com/tumblr_m1kn4vbKV11qbpbbro1_500.gif" alt=""/&gt;&lt;/p&gt;

&lt;h5&gt;Day 2&lt;/h5&gt;

&lt;p&gt;Day 2 was the &lt;em&gt;unconference&lt;/em&gt; day and it started with a casual meeting, in which every participant who had something to present would take a timeslot on a spreadsheet, such that at the end of the meeting we had a relative schedule for the day. After this, everyone was free to go to whichever presentation was most appealing to them.&lt;/p&gt;

&lt;p&gt;And off I went!&lt;/p&gt;

&lt;h3&gt;Early Morning Category Theory &lt;em&gt;with &lt;a href="https://twitter.com/runarorama" target="_blank"&gt;(Rúnar Bjarnason)&lt;/a&gt;&lt;/em&gt;&lt;/h3&gt;

&lt;p&gt;Eager to learn more about Scala and the theoretical background behind it (and being a math nerd), I picked &lt;em&gt;Category Theory&lt;/em&gt; as my first presentation to attend. I did not know much about what &lt;em&gt;Categories&lt;/em&gt; are in Scala and what kind of rules govern these entities, but I went in excited to learn something new.&lt;/p&gt;

&lt;p&gt;The talk started with an abstract definition of &lt;em&gt;Categories&lt;/em&gt; as collections of objects and arrows (or maps/morphisms) that define relationships between the objects. These concepts seemed very familiar to me from those of Set Theory.&lt;/p&gt;

&lt;p&gt;Rúnar went on into describing &lt;em&gt;Functors&lt;/em&gt; (maps between &lt;em&gt;Categories&lt;/em&gt;, that preserve their structure) and then explained what &lt;em&gt;Monads&lt;/em&gt;, &lt;em&gt;Monoids&lt;/em&gt; and &lt;em&gt;Comonads&lt;/em&gt; were, while also doing some whiteboard Scala examples of all of this. These topics were necessary background needed in order to answer a question posed by someone in the audience:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Prove that List is a comonad in the monoid category of List&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Using the information absorbed up to that point, we were able to follow a simple proof of the statement.&lt;/p&gt;

&lt;p&gt;This was my first ever exposure to &lt;em&gt;Category Theory&lt;/em&gt; and I loved it! Rúnar&amp;rsquo;s explanations made me dig more into it after the conference!&lt;/p&gt;

&lt;h3&gt;Scalaz-Stream Workshop &lt;em&gt;with &lt;a href="https://twitter.com/runarorama" target="_blank"&gt;(Rúnar Bjarnason)&lt;/a&gt;&lt;/em&gt;&lt;/h3&gt;

&lt;p&gt;The second presentation I went to was also led by Rúnar Bjarnason, but this time it was very hands-on. He introduced us to &lt;a href="https://github.com/runarorama/ircz" target="_blank"&gt;a small IRC client &amp;amp; server&lt;/a&gt; built using &lt;a href="https://github.com/functional-streams-for-scala/fs2" target="_blank"&gt;scalaz-stream&lt;/a&gt; and everyone in the room had to implement a feature of their preference to add to it.&lt;/p&gt;

&lt;p&gt;I went on trying to implement the ability to change nicknames while in a chatroom. The process of working with &lt;em&gt;scalaz-stream&lt;/em&gt; and, implicitly, with &lt;a href="https://github.com/scalaz/scalaz" target="_blank"&gt;scalaz&lt;/a&gt; was relatively new to me since I haven&amp;rsquo;t written &lt;em&gt;pure&lt;/em&gt; functional code using &lt;em&gt;scalaz&lt;/em&gt; before, but it was a really good experience to be exposed to both of these Scala libraries.&lt;/p&gt;

&lt;p&gt;By the end of the workshop, my implementation ended up being &lt;em&gt;somewhat&lt;/em&gt; functional :)&lt;/p&gt;

&lt;h3&gt;Living with SI-2712 &lt;em&gt;with Daniel Spiewak&lt;/em&gt;&lt;/h3&gt;

&lt;p&gt;After a couple of other presentations, I got to my last presentation of the day. It was about a known Scala compiler bug and it involved a lot of live coding, type-level programming, category theory (d'oh) and just pure amazement.&lt;/p&gt;

&lt;p&gt;The gist of the presentation was a proof of concept of &lt;a href="https://issues.scala-lang.org/browse/SI-2712" target="_blank"&gt;the compiler bug&lt;/a&gt; and &lt;a href="https://gist.github.com/djspiewak/ddbaf065b33d94c68889" target="_blank"&gt;ways to get around it&lt;/a&gt;. It was very well explained and, even though the material got pretty intense towards the end, I enjoyed the deep dive!&lt;/p&gt;

&lt;h5&gt;Conclusions&lt;/h5&gt;

&lt;p&gt;I really enjoyed my time at &lt;em&gt;NE Scala&lt;/em&gt; and learned a lot of new things, from theoretical aspects of Category Theory, to more applied concepts using &lt;em&gt;pure&lt;/em&gt; functional &lt;em&gt;scalaz&lt;/em&gt;. I am very excited for the next conference I&amp;rsquo;m going to attend (or even &lt;em&gt;unconference&lt;/em&gt;, since &lt;em&gt;&lt;a href="http://event.scaladays.org/scaladays-nyc-2016" target="_blank"&gt;Scala Days 2016&lt;/a&gt;&lt;/em&gt; is approaching and will be in NYC :) )!&lt;/p&gt;

&lt;p&gt;&lt;img src="http://cdn.playbuzz.com/cdn/b95e1ace-566a-4150-b20d-1bbb5ae9be4a/7f990315-14a7-4826-85c8-dec119984e46.gif" alt=""/&gt;&lt;/p&gt;</description><link>http://making.meetup.com/post/141623943277</link><guid>http://making.meetup.com/post/141623943277</guid><pubDate>Thu, 24 Mar 2016 17:46:47 -0400</pubDate><category>scala</category><category>conference</category><category>philadelphia</category><category>category theory</category><category>nescala</category><dc:creator>vlad-meetup</dc:creator></item><item><title>We’re hiring!</title><description>&lt;p&gt;Interested in working at Meetup? We worked with our friends at &lt;a href="http://devpost.com/" target="_blank"&gt;DevPost&lt;/a&gt; to put together a page about our engineering team. Find out details about dev process and our tech stack, plus check out interviews with our great engineers.&lt;/p&gt;&lt;p&gt;&lt;a href="http://devpost.com/teams/meetup" target="_blank"&gt;http://devpost.com/teams/meetup&lt;/a&gt;&lt;br/&gt;&lt;/p&gt;</description><link>http://making.meetup.com/post/141213616712</link><guid>http://making.meetup.com/post/141213616712</guid><pubDate>Thu, 17 Mar 2016 15:20:28 -0400</pubDate><category>hiring</category><category>devpost</category><category>engineering-team</category><dc:creator>richmeetup</dc:creator></item><item><title>At NY Historical Society - Silicon City Exhibit</title><description>&lt;p&gt;What happens when you take the Meetup engineering crew to the &lt;a href="http://www.nyhistory.org/exhibitions/silicon-city-computer-history-made-new-york" target="_blank"&gt;NY Historical Society - Silicon City Exhbit?&lt;/a&gt;

&lt;img src="http://photos4.meetupstatic.com/photos/event/b/a/0/a/highres_447467626.jpeg" alt="image"/&gt;

Someone ends up on their modern laptop in the middle of a history lesson, was he just trying to mock the Univac or was he just trying to make sure Meetup was running smoothly!

&lt;img src="http://photos3.meetupstatic.com/photos/event/b/9/e/f/highres_447467599.jpeg" alt="image"/&gt;&lt;/p&gt;&lt;p&gt;
We got to see some older progamming languages and associated mainframes.

&lt;img src="http://photos2.meetupstatic.com/photos/event/c/b/d/6/highres_447472182.jpeg" alt="image"/&gt;&lt;/p&gt;

&lt;p&gt;
We saw punch cards and old satellites, like the Telstar 1, launched in 1962 it successully relayed the first television pictures, telephone calls, and fax images.

&lt;img src="http://photos3.meetupstatic.com/photos/event/c/6/f/8/highres_447470936.jpeg" alt="image"/&gt;&lt;/p&gt;

&lt;p&gt;
All in all it was a great trip through the evolution of technology, which reminds us here at Meetup to continue to evolve our technologies and platform so that we can constantly improve the user experiences our organizers and members have. We love our tech, but we also love building community both inside and out of Meetup.
&lt;/p&gt;

&lt;h3&gt;More pics&lt;/h3&gt;

&lt;p&gt;
New York is home to companies who are evolving the face of technology.
&lt;img src="http://photos3.meetupstatic.com/photos/event/b/b/0/c/highres_447467884.jpeg" alt="image"/&gt;&lt;/p&gt;

&lt;p&gt;
We listen intently as we learn people used to be called computers and eventually as we all know it&amp;rsquo;s what we now call the hardware.
&lt;img src="http://photos3.meetupstatic.com/photos/event/b/8/f/a/highres_447467354.jpeg" alt="image"/&gt;&lt;/p&gt;

&lt;p&gt;
Always be gaming.
&lt;img src="http://photos1.meetupstatic.com/photos/event/b/a/6/6/highres_447467718.jpeg" alt="image"/&gt;&lt;/p&gt;

&lt;p&gt;
Can&amp;rsquo;t blame that on auto-correct.
&lt;img src="http://photos1.meetupstatic.com/photos/event/b/a/a/7/highres_447467783.jpeg" alt="image"/&gt;&lt;/p&gt;

&lt;p&gt;
I think Angry Cat is just lonely.
&lt;img src="http://photos2.meetupstatic.com/photos/event/b/b/1/7/highres_447467895.jpeg" alt="image"/&gt;&lt;/p&gt;</description><link>http://making.meetup.com/post/140422239962</link><guid>http://making.meetup.com/post/140422239962</guid><pubDate>Thu, 03 Mar 2016 21:32:51 -0500</pubDate><category>engineering</category><category>offiste</category><dc:creator>lena-anne-meetup</dc:creator></item><item><title>Futurizing Meetup</title><description>&lt;p&gt;We believe in constantly futurizing Meetup, so we’ve been spending a lot of time prototyping, learning, and making as we move towards &lt;a href="http://making.meetup.com/post/139580873132/making-meetup-even-better" target="_blank"&gt;Rainbows &amp;amp; Unicorns and making Meetup even better&lt;/a&gt;. We hope that by sharing what we’re learning along the way, it helps other engineering teams positively change their company too.&lt;/p&gt;&lt;p&gt;Selecting the right tool for the job is core to our engineering culture. There are five big areas we’ve been focusing on so far, and with each one we’ve been building a little piece of Meetup product as the key part of our assessment.&lt;/p&gt;&lt;p&gt;&lt;b&gt;Build, test, launch, test, build&amp;hellip;&lt;/b&gt;&lt;/p&gt;&lt;p&gt;We&amp;rsquo;re rebuilding the backbone of Meetup &amp;ndash; our continuous delivery pipeline. We decided that in order to empower our engineers with the tools and infrastructure we have boldly envisioned, we’d need to use a new set of CI and automation tools and frameworks. We&amp;rsquo;re currently using &lt;a href="https://jenkins-ci.org/" target="_blank"&gt;Jenkins&lt;/a&gt; which has served us well, but really like what we&amp;rsquo;re seeing with &lt;a href="https://www.atlassian.com/software/bamboo/" target="_blank"&gt;Bamboo&lt;/a&gt; as a complete CI pipeline tool with very high quality plugins and orchestration hooks. &lt;/p&gt;&lt;p&gt;We&amp;rsquo;re reassessing our testing frameworks across our core, web, and mobile platforms so that we have a stronger suite of unit/integration/UI/regression tests with even more coverage.  So far we’ve been really enjoying using &lt;a href="http://www.seleniumhq.org/" target="_blank"&gt;Selenium&lt;/a&gt;, &lt;a href="http://appium.io/" target="_blank"&gt;Appium&lt;/a&gt; for our native apps, and the &lt;a href="http://robotframework.org/" target="_blank"&gt;Robot framework&lt;/a&gt; for acceptance testing.&lt;/p&gt;&lt;p&gt;&lt;b&gt;Moving Meetup to the cloud&lt;/b&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;We&amp;rsquo;re a (mostly) bare metal shop with multiple data centers now. We’re ready to take advantage of the benefits of moving to the cloud including &lt;a href="https://cloud.google.com/compute/docs/autoscaler/" target="_blank"&gt;auto-scaling&lt;/a&gt;, the ability to create &lt;a href="https://en.wikipedia.org/wiki/Phoenix_(mythology)" target="_blank"&gt;phoenix systems&lt;/a&gt; with instances automatically rising out of ones that have crashed and burned, and reducing operational effort and total cost of ownership. &lt;/p&gt;&lt;p&gt;Our infrastructure team has been building our new continuous delivery pipeline and other tools on both &lt;a href="https://aws.amazon.com/" target="_blank"&gt;Amazon Web Services&lt;/a&gt; and &lt;a href="https://cloud.google.com/" target="_blank"&gt;Google Cloud Platform&lt;/a&gt; and we’re close to making a decisions as to which cloud provider we’re selecting.&lt;/p&gt;&lt;p&gt;&lt;b&gt;A world full of services&lt;/b&gt;&lt;/p&gt;&lt;p&gt;Our core engineering team is breaking up our monolithic back-end platform into a service-oriented one.  We&amp;rsquo;re not yet sure how “micro-service” we&amp;rsquo;ll get since we&amp;rsquo;re still figuring out how &lt;a href="http://martinfowler.com/bliki/BoundedContext.html" target="_blank"&gt;bounded contexts&lt;/a&gt; apply to Meetup product, where the &lt;a href="https://www.informit.com/articles/article.aspx?p=359417&amp;amp;seqNum=3" target="_blank"&gt;seams&lt;/a&gt; really are in our codebase, and what parts of our monolith need to scale in bigger or different ways than the others.&lt;/p&gt;&lt;p&gt;We’re very excited to start on the path to learning and making decisions by building our first new service using part of &lt;a href="https://netflix.github.io/" target="_blank"&gt;the Netflix OSS stack&lt;/a&gt;. We did initial prototyping using &lt;a href="https://twitter.github.io/finagle/" target="_blank"&gt;Twitter’s Finagle&lt;/a&gt; and &lt;a href="https://www.typesafe.com/products/typesafe-reactive-platform" target="_blank"&gt;Typesafe’s Reactive Platform&lt;/a&gt; as well, and they’re all great stacks. But we’ve decided on the Netflix OSS and are starting off using &lt;a href="https://github.com/Netflix/hystrix" target="_blank"&gt;Hystrix&lt;/a&gt;, &lt;a href="https://github.com/ReactiveX/RxJava" target="_blank"&gt;RxJava&lt;/a&gt;, &lt;a href="https://github.com/ReactiveX/RxScala" target="_blank"&gt;RxScala&lt;/a&gt;, and &lt;a href="https://github.com/ReactiveX/RxNetty" target="_blank"&gt;RxNetty&lt;/a&gt; to build our first service.&lt;/p&gt;&lt;p&gt;&lt;b&gt;Data is king&lt;/b&gt;&lt;/p&gt;&lt;p&gt;Meetup has happily used &lt;a href="https://www.mysql.com/" target="_blank"&gt;MySQL &lt;/a&gt;as our primary data store for over a decade. Over the years we’ve added things like &lt;a href="http://hadoop.apache.org/" target="_blank"&gt;Hadoop&lt;/a&gt; for distributed computing and &lt;a href="https://aws.amazon.com/redshift/" target="_blank"&gt;Redshift&lt;/a&gt; for data warehousing. We’re at the right time in our growth to evaluate if other data store options will enable us to scale and perform better. We did a lot of research and prototyping with SQL, NoSQL, and distributed NoSQL data stores, and were inspired by the idea of &lt;a href="http://www.confluent.io/blog/turning-the-database-inside-out-with-apache-samza/" target="_blank"&gt;turning the database inside-out&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;We decided that we’d learn the most valuable information if we built our first new service using a NoSQL distributed data store like &lt;a href="http://cassandra.apache.org/" target="_blank"&gt;Apache Cassandra&lt;/a&gt;, &lt;a href="https://aws.amazon.com/dynamodb/" target="_blank"&gt;Amazon Dynamo&lt;/a&gt;, or &lt;a href="https://cloud.google.com/datastore/" target="_blank"&gt;Google Cloud Datastore&lt;/a&gt;, which we’re internally calling Cassandra* for short. Our decision on which one to use will align with our impending our cloud provider decision and until then we’re prototyping using Meetupy data with each.  &lt;/p&gt;&lt;p&gt;&lt;b&gt;React or not here we come&lt;/b&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;Our web engineering team has been prototyping both &lt;a href="http://emberjs.com/" target="_blank"&gt;Ember&lt;/a&gt; and &lt;a href="https://facebook.github.io/react/" target="_blank"&gt;React&lt;/a&gt; by building a small part of Meetup web product with each. After that was done, and a lot of debate throughout the whole team, we’ve decided on React as our new JavaScript web framework.  &lt;/p&gt;&lt;p&gt;We’re super excited to be building an &lt;a href="http://isomorphic.net/" target="_blank"&gt;isomorphic JavaScript&lt;/a&gt; web stack to facilitate iterating web product in a faster and more productive way. We especially like the React ecosystem, separation of concerns, and preference for small, sharp tools.&lt;/p&gt;&lt;p&gt;&lt;b&gt;Futurize Meetup with us&lt;/b&gt;&lt;/p&gt;&lt;p&gt;While we’ve answered many questions, we still have a long way to go.  We’re thrilled to work at a company where we have the time to prototype different technologies and build towards the best answers for both current and future Meetup product. As we progress, we&amp;rsquo;ll be writing posts that dive into the specifics of what we’re learning, the tools, technologies, and frameworks we&amp;rsquo;re using, and how they&amp;rsquo;re working for us.&lt;br/&gt;&lt;/p&gt;&lt;p&gt;If you want to build the future of Meetup with us, learn and experiment with new technologies, or have ideas to share, check out ways to &lt;a href="http://www.meetup.com/jobs/" target="_blank"&gt;join our team&lt;/a&gt;. &lt;/p&gt;</description><link>http://making.meetup.com/post/140389877512</link><guid>http://making.meetup.com/post/140389877512</guid><pubDate>Thu, 03 Mar 2016 09:40:42 -0500</pubDate><category>infrastructure</category><category>cloud computing</category><category>microservices</category><category>tdd</category><category>testing</category><category>automation</category><category>javascript</category><category>continuous delivery</category><dc:creator>yvette-meetup</dc:creator></item><item><title>Hackathon!</title><description>&lt;p&gt;Last week, Meetup&amp;rsquo;s engineers took 48 hours off from their normal schedules in order to participate in our quarterly hackathon!&lt;/p&gt;

&lt;p&gt;The theme of this hackathon was &amp;lsquo;Rainbows and Unicorns&amp;rsquo;, in reference to our remaking of the Meetup platform that you can read about &lt;a href="http://making.meetup.com/post/139580873132/making-meetup-even-better" target="_blank"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Check out some photos of the team in action:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://i.imgur.com/hWbM59s.jpg" alt="day1"/&gt;&lt;img src="https://i.imgur.com/jNaCyhC.jpg" alt="day2"/&gt;&lt;img src="https://i.imgur.com/SSPDVuv.jpg" alt="day3"/&gt;&lt;/p&gt;

&lt;p&gt;At the end of the 48 hours, we all demoed our projects, whether they were successful or not!&lt;/p&gt;

&lt;p&gt;&lt;img src="http://imgur.com/li7MHE0.jpg" alt="demo"/&gt;&lt;/p&gt;

&lt;p&gt;And gave out these awesome Rainbows and Unicorns-themed prizes to the winners (yep, those are LED-lit horns):
&lt;img src="http://imgur.com/u5MKNjA.jpg" alt="prizes"/&gt;&lt;/p&gt;

&lt;hr&gt;&lt;p&gt;The winners were:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Most Ridiculous:&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Nathan Stilwell&lt;/strong&gt; - Magic Pipe
&lt;img src="https://i.imgur.com/y9DZjCq.jpg" alt="nathan"/&gt;&lt;/p&gt;

&lt;hr&gt;&lt;p&gt;&lt;em&gt;Most on theme:&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Evan Estola/Matt Kime&lt;/strong&gt; - Meetup on Alexa
&lt;img src="https://i.imgur.com/LLvlgtu.jpg" alt="evan"/&gt;&lt;/p&gt;

&lt;hr&gt;&lt;p&gt;&lt;em&gt;Best Overall:&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rick Boenigk&lt;/strong&gt; - React Native Door Check
&lt;img src="https://i.imgur.com/elIo7xu.jpg" alt="rick"/&gt;&lt;/p&gt;

&lt;hr&gt;&lt;p&gt;We had our fair share of ridiculous hacks, as well as practical ones.  It was a lot of fun, and a lot of cool ideas came out of it.  Many of them might even get launched in some form or another!&lt;/p&gt;</description><link>http://making.meetup.com/post/139922861622</link><guid>http://making.meetup.com/post/139922861622</guid><pubDate>Wed, 24 Feb 2016 14:22:25 -0500</pubDate><category>meetup</category><category>hackathon</category><category>rainbows</category><category>unicorns</category><category>alexa</category><category>react</category><category>echo</category><dc:creator>jakemeetup</dc:creator></item><item><title>Making Meetup Even Better</title><description>&lt;p&gt;We&amp;rsquo;re always making Meetup better. For 14 years, we&amp;rsquo;ve been growing, evolving and making Meetup a little better every day, with every release. This year, we&amp;rsquo;ve kicked off one of the biggest endeavors in Meetup&amp;rsquo;s history to make Meetup even better. &lt;/p&gt;&lt;p&gt;We&amp;rsquo;re remaking the Meetup platform, infrastructure, and dev tools. Our objective is to empower our engineers to be more productive and love using our dev tools to make and ship product faster and better than ever before.&lt;/p&gt;&lt;p&gt;When we kicked this off, we did a lot of brainstorming, research, talking, listening, prototyping, and drew a &lt;i&gt;lot&lt;/i&gt; of diagrams. The very first diagram we drew and took a picture of was this (raw, not doctored) photo:&lt;/p&gt;&lt;figure data-orig-width="3264" data-orig-height="2448" class="tmblr-full"&gt;&lt;img src="http://41.media.tumblr.com/6f37289eb189aea4dc6906d8300cfa30/tumblr_inline_o2rx6oQDY31u0rkvc_540.jpg" alt="image" data-orig-width="3264" data-orig-height="2448"/&gt;&lt;/figure&gt;&lt;p&gt;What did we do when confronted with an unexpected rainbow shining right through our core platform as we drew up ways we’d re-architect it? Well, we of course named the remaking of Meetup the &lt;a href="https://www.google.com/search?q=rainbows+and+unicorns+project&amp;amp;espv=2&amp;amp;biw=1427&amp;amp;bih=778&amp;amp;source=lnms&amp;amp;tbm=isch&amp;amp;sa=X&amp;amp;ved=0ahUKEwjCwMiC7ILLAhUF8x4KHaw8BUYQ_AUIBigB&amp;amp;dpr=1#tbm=isch&amp;amp;q=rainbows+and+unicorns" target="_blank"&gt;&lt;i&gt;Rainbows &amp;amp; Unicorns&lt;/i&gt; &lt;/a&gt;project.&lt;/p&gt;&lt;p&gt;As we progress, we’ll be posting more specifics around what we’re prototyping, learning, and making as we move towards Rainbows &amp;amp; Unicorns. We hope that by sharing what we’re learning along the way, it helps others who might be doing something similar.&lt;/p&gt;&lt;p&gt;This week, we’re all busy with one of our quarterly &lt;a href="http://tmblr.co/ZnX14y21-7bqK" target="_blank"&gt;Meetup-wide hackathons&lt;/a&gt;, aptly themed: &lt;i&gt;Rainbows &amp;amp; Unicorns: futurizing Meetup&lt;/i&gt;. Stay tuned for more about what we’re hacking and making &amp;ndash; it’s guaranteed to be quite colorful and fun!&lt;/p&gt;</description><link>http://making.meetup.com/post/139580873132</link><guid>http://making.meetup.com/post/139580873132</guid><pubDate>Thu, 18 Feb 2016 22:47:16 -0500</pubDate><category>rainbows</category><category>unicorns</category><category>replatform</category><category>meetup</category><category>infrastructure</category><category>hackathon</category><dc:creator>yvette-meetup</dc:creator></item><item><title>hackathon progress pic.twitter.com/rXkm5FWkU0— Adam Detrick (@akdetrick) February 18, 2016
</title><description>&lt;blockquote class="twitter-tweet" data-lang="en"&gt;&lt;p lang="en" dir="ltr"&gt;hackathon progress &lt;a href="https://t.co/rXkm5FWkU0" target="_blank"&gt;pic.twitter.com/rXkm5FWkU0&lt;/a&gt;&lt;/p&gt;— Adam Detrick (@akdetrick) &lt;a href="https://twitter.com/akdetrick/status/700444364313645056" target="_blank"&gt;February 18, 2016&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async src="//platform.twitter.com/widgets.js" charset="utf-8"&gt;&lt;/script&gt;</description><link>http://making.meetup.com/post/139571649812</link><guid>http://making.meetup.com/post/139571649812</guid><pubDate>Thu, 18 Feb 2016 19:43:34 -0500</pubDate><dc:creator>brianmeetup</dc:creator></item><item><title>Sorting Out Japanese</title><description>&lt;p&gt;&lt;img src="http://i.imgur.com/0eFkdmG.jpg" width="100%" alt="head"/&gt;&lt;/p&gt;

&lt;p&gt;For years, Meetup has been available in 6 different languages.  These are:&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;English&lt;/li&gt;
&lt;li&gt;French&lt;/li&gt;
&lt;li&gt;German&lt;/li&gt;
&lt;li&gt;Italian&lt;/li&gt;
&lt;li&gt;Portuguese &lt;/li&gt;
&lt;li&gt;Spanish&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;And while these languages all have their own nuances and challenges when it comes to localizing for them, it was poor preparation for some of the issues we would run into when localizing for our next supported language&amp;hellip;&lt;/p&gt;

&lt;h2&gt;Japanese&lt;/h2&gt;

&lt;p&gt;Yes, that is right, Meetup is now available in it’s 7th supported language, Japanese!  It is our first supported language with a non-latin-based alphabet.&lt;/p&gt;

&lt;p&gt;We have run into many issues because of this, but the most interesting presented itself when it came time to implement alphabetical sorting.&lt;/p&gt;

&lt;h2&gt;Alphabetical Sorting&lt;/h2&gt;

&lt;p&gt;On first glance, alphabetically sorting a list of strings seems like a trivial task.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; Ordering
  .natural()
  .sortedCopy(["eagle","cat","dog","bird","falcon","duck"])
[bird, cat, dog, duck, eagle, falcon]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;However many things can actually complicate this, such as capitalized letters:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; Ordering
  .natural()
  .sortedCopy(["eagle","cat","dog","bird","falcon","duck","Dog"])
[Dog, bird, cat, dog, duck, eagle, falcon]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;A user would expect Dog and dog to be sorted next to each other.  However, because these strings are sorted by character code, this is not the case.&lt;/p&gt;

&lt;p&gt;To do a more language-friendly sort, you can use something called a collator.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; Ordering
  .from(Collator.getInstance(Locale.US))
  .sortedCopy(["eagle","cat","dog","bird","falcon","duck","Dog"])
[bird, cat, dog, Dog, duck, eagle, falcon]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This performs the sorting based on rules from the American English locale.
So how about Japanese?&lt;/p&gt;

&lt;h2&gt;Japanese Alphabets&lt;/h2&gt;

&lt;p&gt;The first thing to note is that there are multiple alphabets in play here:&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;Kanji: Thousands of symbols borrowed from Chinese and that can form words. Ex: 東京  - 東 means East and 京 means Capital city, so that’s Tokyo).&lt;/li&gt;
&lt;li&gt;Hiragana: Phonetic script (46 characters), for words that can not be described with Kanji, or to make the pronunciation of a word using kanji transparent. Each character corresponds to a sound, like ma, ka, sa…. Ex.: とうきょう  (To-ky-o)&lt;/li&gt;
&lt;li&gt;Katakana: Same as Hiragana (48 characters) but to represent the pronunciation of foreign words. Ex.: マクドナルド (ma-ku-do-na-ru-do, which is how the Japanese would pronounce McDonalds)&lt;/li&gt;
&lt;li&gt;Romaji: Latin characters, used to make the pronunciation of Japanese words transparent for those who can’t read the Japanese alphabets (ex: Tōkyō), or used to look trendy, foreign, in some cases. &lt;/li&gt;
&lt;/ul&gt;&lt;h2&gt;Japanese ordering&lt;/h2&gt;

&lt;p&gt;Strings in Japanese can be made up of characters in any of these alphabets, and using ‘dumb’ sorting, these alphabets wouldn’t sort together at all.  However you can use a collator based on the Japanese locale, and that gets you pretty far already.&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;健康 (Health)&lt;/li&gt;
&lt;li&gt;ダンス (Dancing)&lt;/li&gt;
&lt;li&gt;テクノロジー (Tech)&lt;/li&gt;
&lt;li&gt;家族 (Family)&lt;/li&gt;
&lt;li&gt;ゲーム (Games)&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;Once sorted with the collator, it becomes:&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;ゲーム (Games)&lt;/li&gt;
&lt;li&gt;ダンス (Dancing)&lt;/li&gt;
&lt;li&gt;テクノロジー (Tech)&lt;/li&gt;
&lt;li&gt;健康 (Health)&lt;/li&gt;
&lt;li&gt;家族 (Family)&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;See Below:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;x = [u”健康”, u”ダンス”, u”テクノロジー”, u”家族”, u”ゲーム”]
&amp;gt;&amp;gt;&amp;gt; Ordering
  .from(Collator.getInstance(Locale.JAPAN))
  .sortedCopy(x)
[ゲーム, ダンス, テクノロジー, 家族, 健康]
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;The problem&lt;/h2&gt;

&lt;p&gt;This will sort words starting with Katakana and Hiragana correctly, but will always put the words starting with a Kanji at the bottom of the list. 
Why? Because a computer does not know how a Kanji is pronounced, and ordering in Japanese is based on pronunciation, following the strict Gojūon (literally, “fifty sounds”) ordering table for kana alphabets. First should come the words starting with the sound a then with i, u, e, ka, ki, ku, etc.). 
With Kanji, the characters used do not each correspond to the same sound every time. The pronunciation changes depending on context, and currently computers can not guess that.&lt;/p&gt;

&lt;p&gt;Ex.: 今日 can be read as きょう (kyō) or こんにち (konnichi).&lt;/p&gt;

&lt;h2&gt;The solution&lt;/h2&gt;

&lt;p&gt;Given a large list of arbitrary kanji strings, we would be unable to sort them correctly without human intervention giving us an indication of how it’s pronounced in its context.  But luckily for us, we don’t have a large list of arbitrary strings! We have a relatively small list of static, already known strings.  So we did in fact have a solution.&lt;br/&gt;
The thing we needed to sort was the list of categories that Meetup Groups can belong to. We just needed the phonetic spellings of our categories which had Kanji-based names, as we learned earlier that any Kanji-based name can be converted into a more transparent phonetic version.&lt;/p&gt;

&lt;p&gt;We asked our translators to help provide these, and then created a new locale called ‘Japanese Phonetic&amp;rsquo; (ja-x-phonetic) and a new translation file containing these phonetic translations.  And then, when ordering the list on the backend, we temporarily translated the categories into ja-x-phonetic, sorted them using a Japanese collator, and then re-translated them back into normal Japanese while maintaining the same order. Et Voilà!&lt;/p&gt;

&lt;p&gt;&lt;img src="http://i.imgur.com/hgto7pr.png" width="100%" alt="cat"/&gt;&lt;/p&gt;

&lt;h2&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;So we solved our problem, but this is an issue which continues to exist for things like user generated content.  If we had a large list of member names in Kanji that we wanted to sort, this would be impossible to do without either transliterating each of these ourselves (not scalable), or prompting users to provide phonetic spellings of their names in addition to the Kanji one (more scalable).&lt;/p&gt;

&lt;p&gt;This issue came up unexpectedly, but taught us a good lesson about not making any assumptions when dealing with language.  If you want to develop a global product, you have to deal with your product in a global way, and that means building things that make sense in ALL languages.&lt;/p&gt;</description><link>http://making.meetup.com/post/131750615312</link><guid>http://making.meetup.com/post/131750615312</guid><pubDate>Fri, 23 Oct 2015 11:11:05 -0400</pubDate><category>meetup</category><category>japan</category><category>japanese</category><category>internationalization</category><category>sorting</category><dc:creator>jakemeetup</dc:creator></item><item><title>Meetup and Slack</title><description>&lt;p&gt;Meetup recently made the switch to &lt;a href="https://slack.com/" target="_blank"&gt;Slack&lt;/a&gt; for our team communication platform and our clearinghouse of puppy gifs. One of my favorite features on Slack is how incredibly easy it is to create &lt;a href="https://slack.com/integrations" target="_blank"&gt;integrations&lt;/a&gt;. In order to get familiar with how the webhook system works, I decided to write a little ruby app that uses the &lt;a href="http://www.meetup.com/meetup_api/" target="_blank"&gt;Meetup API&lt;/a&gt; to post the next available Meetup event for a given group. If you are an organizer who is using a Slack team to facilitate conversation outside of your Meetup events, host Meetups in your office, or just want to let your coworkers know when something interesting is happening, this might come in handy. Enjoy!&lt;br/&gt;&lt;/p&gt;&lt;p&gt;&lt;a href="https://github.com/jeffcampbell/meetup-slack" target="_blank"&gt;https://github.com/jeffcampbell/meetup-slack&lt;/a&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;&lt;i&gt;Jeff is a Senior QA Engineer at Meetup&lt;/i&gt;&lt;/p&gt;</description><link>http://making.meetup.com/post/130161038177</link><guid>http://making.meetup.com/post/130161038177</guid><pubDate>Tue, 29 Sep 2015 18:47:48 -0400</pubDate><category>Meetup</category><category>Slack</category><dc:creator>meetupjeff</dc:creator></item><item><title>Apple In-App Purchase Mock Service (Fake it ‘till you make it)</title><description>&lt;p&gt;&lt;b&gt;A configurable sandbox to test auto-renewing Apple IAP subscriptions&lt;/b&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;&lt;b&gt;TLDR;&lt;/b&gt; We made a thing that pretends to be an Apple In-App Purchase service to make it easier for people to test their code. It’s open sourced and can be found here: &lt;a href="https://github.com/meetup/apple-of-my-iap" target="_blank"&gt;https://github.com/meetup/apple-of-my-iap&lt;/a&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;&lt;b&gt;The long story:&lt;/b&gt;&lt;/p&gt;&lt;p&gt;As part of Meetup’s iOS offering, we recently launched the ability for our members to use Apple In-App purchases (IAP) when buying Meetup subscriptions.&lt;/p&gt;&lt;p&gt;&lt;b&gt;Implementing this feature was a lot of fun. The same kind of fun as running through a gauntlet full of sharp axes, hot flames, and killer piranhas.&lt;/b&gt;&lt;/p&gt;&lt;figure class="tmblr-full" data-orig-height="281" data-orig-width="500"&gt;&lt;img src="http://38.media.tumblr.com/8bb40c29111d11d8ffc48aedcb966cba/tumblr_inline_ntr31mpVyT1r7wad1_500.gif" data-orig-height="281" data-orig-width="500"/&gt;&lt;/figure&gt;&lt;p&gt;But before we get into that…. &lt;br/&gt;&lt;/p&gt;&lt;h2&gt;&lt;b&gt;What exactly is an Apple In-App Purchase, and more importantly why do I care? (Hint: It’s not Apple Pay)&lt;/b&gt;&lt;/h2&gt;&lt;p&gt;An Apple In-App Purchase (IAP) is a purchase of digital goods or services through Apple’s native payment processing platform available on all iOS devices. If you’re an iOS user and you’ve ever been prompted by this screen: &lt;/p&gt;&lt;figure class="tmblr-full" data-orig-height="480" data-orig-width="320"&gt;&lt;img src="http://36.media.tumblr.com/07359037da2b2b478765a800e1a45cac/tumblr_inline_ntr31ysTiP1r7wad1_540.png" data-orig-height="480" data-orig-width="320"/&gt;&lt;/figure&gt;&lt;p&gt;then chances are you’ve made an In-App Purchase before. Typical use cases of include:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;unlocking premium features within an application&lt;/li&gt;&lt;li&gt;purchasing extra levels or items from within a game&lt;/li&gt;&lt;li&gt;purchasing a subscription to digital content&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Since a customer’s payment information is already stored in their iTunes account, they have the ability to make quick and painless payments without having to pull out their wallet and re-enter credit card number information. One tap and they’re done. This also means that you don’t have to spend time designing, building, and maintaining finicky payment forms. &lt;/p&gt;&lt;p&gt;And if that’s not enough incentive for you, Apple will kick you out of the iTunes store for attempting to sell digital goods/services in any other manner other than through IAP.&lt;/p&gt;&lt;figure class="tmblr-full" data-orig-height="211" data-orig-width="320"&gt;&lt;img src="http://33.media.tumblr.com/80c9d177ffda40e3e2d3a3e2cbd02786/tumblr_inline_ntr32tCaX11r7wad1_500.gif" data-orig-height="211" data-orig-width="320"/&gt;&lt;/figure&gt;&lt;p&gt;Sounds like a deal to me.&lt;/p&gt;&lt;h2&gt;&lt;b&gt;Meetup meets IAP&lt;/b&gt;&lt;/h2&gt;&lt;p&gt;Here at Meetup, we elected to use the auto-renewing feature of Apple IAP, which means that Apple will automagically take care of all the nonsense that comes with scheduling and charging subscription payments. Hooray!&lt;br/&gt;&lt;/p&gt;&lt;p&gt;All we have to do is ask Apple who paid for what, and then ensure that we are delivering the right service and products to the right people. Since the Meetup platform exists on the web as well as on both iOS and Android, we delegate subscription management to the server in order to ensure a uniform experience across all 3 platforms. &lt;br/&gt;&lt;/p&gt;&lt;p&gt;So for any given member with a Meetup subscription bought through IAP, we query Apple’s iTunes endpoint and receive a response similar to this:&lt;br/&gt;&lt;/p&gt;&lt;p&gt;{&lt;br/&gt;    &amp;quot;status&amp;quot;: 0,&lt;br/&gt;    &amp;quot;environment&amp;quot;: &amp;ldquo;Sandbox&amp;rdquo;,&lt;br/&gt;    &amp;quot;receipt&amp;quot;: {&lt;br/&gt;        &amp;quot;receiptType&amp;quot;: &amp;ldquo;ProductionSandbox&amp;rdquo;,&lt;br/&gt;        &amp;quot;adamId&amp;quot;: 0,&lt;br/&gt;        &amp;quot;appItemId&amp;quot;: 0,&lt;br/&gt;        &amp;quot;bundleId&amp;quot;: &amp;ldquo;com.meetup.iphone&amp;rdquo;,&lt;br/&gt;        &amp;quot;applicationVersion&amp;quot;: &amp;ldquo;409000&amp;rdquo;,&lt;br/&gt;        &amp;quot;downloadId&amp;quot;: 0,&lt;br/&gt;        &amp;quot;versionExternalIdentifier&amp;quot;: 0,&lt;br/&gt;        &amp;quot;requestDate&amp;quot;: &amp;ldquo;2015-04-23 23:33:34 Etc/GMT&amp;rdquo;,&lt;br/&gt;        &amp;quot;requestDateMs&amp;quot;: &amp;ldquo;1429832014510&amp;rdquo;,&lt;br/&gt;        &amp;quot;requestDatePst&amp;quot;: &amp;ldquo;2015-04-23 16:33:34 America/Los_Angeles&amp;rdquo;,&lt;br/&gt;        &amp;quot;originalPurchaseDate&amp;quot;: &amp;ldquo;2013-08-01 07:00:00 Etc/GMT&amp;rdquo;,&lt;br/&gt;        &amp;quot;originalPurchaseDateMs&amp;quot;: &amp;ldquo;1375340400000&amp;rdquo;,&lt;br/&gt;        &amp;quot;originalPurchaseDatePst&amp;quot;: &amp;ldquo;2013-08-01 00:00:00 America/Los_Angeles&amp;rdquo;,&lt;br/&gt;        &amp;quot;originalApplicationVersion&amp;quot;: &amp;ldquo;1.0&amp;rdquo;,&lt;br/&gt;        &amp;quot;inApp&amp;quot;: [&lt;br/&gt;            {&lt;br/&gt;                &amp;quot;quantity&amp;quot;: &amp;ldquo;1&amp;rdquo;,&lt;br/&gt;                &amp;quot;productId&amp;quot;: &amp;ldquo;com.meetup.iphone.test.7days&amp;rdquo;,&lt;br/&gt;                &amp;quot;transactionId&amp;quot;: &amp;ldquo;1000000151887289&amp;rdquo;,&lt;br/&gt;                &amp;quot;originalTransactionId&amp;quot;: &amp;ldquo;1000000151887289&amp;rdquo;,&lt;br/&gt;                &amp;quot;purchaseDate&amp;quot;: &amp;ldquo;2015-04-15 21:46:04 Etc/GMT&amp;rdquo;,&lt;br/&gt;                &amp;quot;purchaseDateMs&amp;quot;: &amp;ldquo;1429134364000&amp;rdquo;,&lt;br/&gt;                &amp;quot;purchaseDatePst&amp;quot;: &amp;ldquo;2015-04-15 14:46:04 America/Los_Angeles&amp;rdquo;,&lt;br/&gt;                &amp;quot;originalPurchaseDate&amp;quot;: &amp;ldquo;2015-04-15 21:46:06 Etc/GMT&amp;rdquo;,&lt;br/&gt;                &amp;quot;originalPurchaseDateMs&amp;quot;: &amp;ldquo;1429134366000&amp;rdquo;,&lt;br/&gt;                &amp;quot;originalPurchaseDatePst&amp;quot;: &amp;ldquo;2015-04-15 14:46:06 America/Los_Angeles&amp;rdquo;,&lt;br/&gt;                &amp;quot;expiresDate&amp;quot;: &amp;ldquo;2015-04-15 21:49:04 Etc/GMT&amp;rdquo;,&lt;br/&gt;                &amp;quot;expiresDateMs&amp;quot;: &amp;ldquo;1429134544000&amp;rdquo;,&lt;br/&gt;                &amp;quot;expiresDatePst&amp;quot;: &amp;ldquo;2015-04-15 14:49:04 America/Los_Angeles&amp;rdquo;,&lt;br/&gt;                &amp;quot;webOrderLineItemId&amp;quot;: &amp;ldquo;1000000029487694&amp;rdquo;,&lt;br/&gt;                &amp;quot;isTrialPeriod&amp;quot;: &amp;ldquo;false&amp;rdquo;&lt;br/&gt;            },&lt;br/&gt;            {&lt;br/&gt;                &amp;quot;quantity&amp;quot;: &amp;ldquo;1&amp;rdquo;,&lt;br/&gt;                &amp;quot;productId&amp;quot;: &amp;ldquo;com.meetup.iphone.test.7days&amp;rdquo;,&lt;br/&gt;                &amp;quot;transactionId&amp;quot;: &amp;ldquo;1000000151887546&amp;rdquo;,&lt;br/&gt;                &amp;quot;originalTransactionId&amp;quot;: &amp;ldquo;1000000151887289&amp;rdquo;,&lt;br/&gt;                &amp;quot;purchaseDate&amp;quot;: &amp;ldquo;2015-04-15 21:49:04 Etc/GMT&amp;rdquo;,&lt;br/&gt;                &amp;quot;purchaseDateMs&amp;quot;: &amp;ldquo;1429134544000&amp;rdquo;,&lt;br/&gt;                &amp;quot;purchaseDatePst&amp;quot;: &amp;ldquo;2015-04-15 14:49:04 America/Los_Angeles&amp;rdquo;,&lt;br/&gt;                &amp;quot;originalPurchaseDate&amp;quot;: &amp;ldquo;2015-04-15 21:48:06 Etc/GMT&amp;rdquo;,&lt;br/&gt;                &amp;quot;originalPurchaseDateMs&amp;quot;: &amp;ldquo;1429134486000&amp;rdquo;,&lt;br/&gt;                &amp;quot;originalPurchaseDatePst&amp;quot;: &amp;ldquo;2015-04-15 14:48:06 America/Los_Angeles&amp;rdquo;,&lt;br/&gt;                &amp;quot;expiresDate&amp;quot;: &amp;ldquo;2015-04-15 21:52:04 Etc/GMT&amp;rdquo;,&lt;br/&gt;                &amp;quot;expiresDateMs&amp;quot;: &amp;ldquo;1429134724000&amp;rdquo;,&lt;br/&gt;                &amp;quot;expiresDatePst&amp;quot;: &amp;ldquo;2015-04-15 14:52:04 America/Los_Angeles&amp;rdquo;,&lt;br/&gt;                &amp;quot;webOrderLineItemId&amp;quot;: &amp;ldquo;1000000029487695&amp;rdquo;,&lt;br/&gt;                &amp;quot;isTrialPeriod&amp;quot;: &amp;ldquo;false&amp;rdquo;&lt;br/&gt;            }&lt;br/&gt;        ]&lt;br/&gt;    },&lt;br/&gt;    &amp;quot;latestReceiptInfo&amp;quot;: [&lt;br/&gt;        {&lt;br/&gt;            &amp;quot;quantity&amp;quot;: &amp;ldquo;1&amp;rdquo;,&lt;br/&gt;            &amp;quot;productId&amp;quot;: &amp;ldquo;com.meetup.iphone.test.7days&amp;rdquo;,&lt;br/&gt;            &amp;quot;transactionId&amp;quot;: &amp;ldquo;1000000151887289&amp;rdquo;,&lt;br/&gt;            &amp;quot;originalTransactionId&amp;quot;: &amp;ldquo;1000000151887289&amp;rdquo;,&lt;br/&gt;            &amp;quot;purchaseDate&amp;quot;: &amp;ldquo;2015-04-15 21:46:04 Etc/GMT&amp;rdquo;,&lt;br/&gt;            &amp;quot;purchaseDateMs&amp;quot;: &amp;ldquo;1429134364000&amp;rdquo;,&lt;br/&gt;            &amp;quot;purchaseDatePst&amp;quot;: &amp;ldquo;2015-04-15 14:46:04 America/Los_Angeles&amp;rdquo;,&lt;br/&gt;            &amp;quot;originalPurchaseDate&amp;quot;: &amp;ldquo;2015-04-15 21:46:06 Etc/GMT&amp;rdquo;,&lt;br/&gt;            &amp;quot;originalPurchaseDateMs&amp;quot;: &amp;ldquo;1429134366000&amp;rdquo;,&lt;br/&gt;            &amp;quot;originalPurchaseDatePst&amp;quot;: &amp;ldquo;2015-04-15 14:46:06 America/Los_Angeles&amp;rdquo;,&lt;br/&gt;            &amp;quot;expiresDate&amp;quot;: &amp;ldquo;2015-04-15 21:49:04 Etc/GMT&amp;rdquo;,&lt;br/&gt;            &amp;quot;expiresDateMs&amp;quot;: &amp;ldquo;1429134544000&amp;rdquo;,&lt;br/&gt;            &amp;quot;expiresDatePst&amp;quot;: &amp;ldquo;2015-04-15 14:49:04 America/Los_Angeles&amp;rdquo;,&lt;br/&gt;            &amp;quot;webOrderLineItemId&amp;quot;: &amp;ldquo;1000000029487694&amp;rdquo;,&lt;br/&gt;            &amp;quot;isTrialPeriod&amp;quot;: &amp;ldquo;false&amp;rdquo;&lt;br/&gt;        },&lt;br/&gt;        {&lt;br/&gt;            &amp;quot;quantity&amp;quot;: &amp;ldquo;1&amp;rdquo;,&lt;br/&gt;            &amp;quot;productId&amp;quot;: &amp;ldquo;com.meetup.iphone.test.7days&amp;rdquo;,&lt;br/&gt;            &amp;quot;transactionId&amp;quot;: &amp;ldquo;1000000151887546&amp;rdquo;,&lt;br/&gt;            &amp;quot;originalTransactionId&amp;quot;: &amp;ldquo;1000000151887289&amp;rdquo;,&lt;br/&gt;            &amp;quot;purchaseDate&amp;quot;: &amp;ldquo;2015-04-15 21:49:04 Etc/GMT&amp;rdquo;,&lt;br/&gt;            &amp;quot;purchaseDateMs&amp;quot;: &amp;ldquo;1429134544000&amp;rdquo;,&lt;br/&gt;            &amp;quot;purchaseDatePst&amp;quot;: &amp;ldquo;2015-04-15 14:49:04 America/Los_Angeles&amp;rdquo;,&lt;br/&gt;            &amp;quot;originalPurchaseDate&amp;quot;: &amp;ldquo;2015-04-15 21:48:06 Etc/GMT&amp;rdquo;,&lt;br/&gt;            &amp;quot;originalPurchaseDateMs&amp;quot;: &amp;ldquo;1429134486000&amp;rdquo;,&lt;br/&gt;            &amp;quot;originalPurchaseDatePst&amp;quot;: &amp;ldquo;2015-04-15 14:48:06 America/Los_Angeles&amp;rdquo;,&lt;br/&gt;            &amp;quot;expiresDate&amp;quot;: &amp;ldquo;2015-04-15 21:52:04 Etc/GMT&amp;rdquo;,&lt;br/&gt;            &amp;quot;expiresDateMs&amp;quot;: &amp;ldquo;1429134724000&amp;rdquo;,&lt;br/&gt;            &amp;quot;expiresDatePst&amp;quot;: &amp;ldquo;2015-04-15 14:52:04 America/Los_Angeles&amp;rdquo;,&lt;br/&gt;            &amp;quot;webOrderLineItemId&amp;quot;: &amp;ldquo;1000000029487695&amp;rdquo;,&lt;br/&gt;            &amp;quot;isTrialPeriod&amp;quot;: &amp;ldquo;false&amp;rdquo;&lt;br/&gt;        }&lt;br/&gt;    ],&lt;br/&gt;    &amp;quot;latestReceipt&amp;quot;: &amp;ldquo;MIJA5AYJKoZIhvcNAQcCoIJA1TCCQNECAQEx &amp;hellip;&amp;rdquo;&lt;br/&gt;}&lt;/p&gt;&lt;h2&gt;&lt;b&gt;The Problem: Testing with IAP&lt;/b&gt;&lt;/h2&gt;&lt;p&gt;Apple provides an iTunes sandbox environment which can be used to test In-App Purchases without spending real money. For auto-renewing subscriptions, the sandbox environment allows you to create a subscription which then automatically renew itself six times before expiring. Since many subscriptions periods last one week at the very least, Apple provides shorter test durations for the sandbox environment which are mapped to production durations in the following manner:&lt;/p&gt;&lt;p&gt;&lt;b&gt;Production -&amp;gt; Sandbox&lt;/b&gt;&lt;/p&gt;&lt;p&gt;1 week        -&amp;gt;  3 minutes&lt;br/&gt;&lt;/p&gt;&lt;p&gt;1 month      -&amp;gt;  5 minutes&lt;/p&gt;&lt;p&gt;2 months    -&amp;gt;  10 minutes&lt;/p&gt;&lt;p&gt;3 months    -&amp;gt;  15 minutes&lt;/p&gt;&lt;p&gt;6 months    -&amp;gt;  30 minutes&lt;/p&gt;&lt;p&gt;1 year         -&amp;gt;  1 hour&lt;/p&gt;&lt;p&gt;However, there were some serious limitations with the iTunes sandbox which made it difficult to develop and test for, especially for non-iOS engineers like myself. &lt;br/&gt;&lt;/p&gt;&lt;ol&gt;&lt;li&gt;You need to have an iOS device with IAP functionality already built into your app in order to simulate an IAP purchase. This would require our iOS engineers to try and build IAP functionality into the app before any backend support for it existed.&lt;br/&gt;&lt;/li&gt;&lt;li&gt;It’s impossible to manipulate test subscriptions once they have been created in the sandbox. Testing edge cases such as refunds, cancellations, early renewals, late renewals, etc. are only doable by writing mock subscription creation cases within the iOS app. This would require our iOS engineers to spend extra time creating tooling around a feature that they couldn’t yet test. &lt;br/&gt;&lt;/li&gt;&lt;li&gt;The shortest amount of time it takes for a subscription to expire is 18 minutes, which is particularly irritating when you’re trying to debug expiration logic in your application.&lt;br/&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Points #1 and #2 would have created heavy inter-team dependencies between our iOS engineers and our backend engineers, something which would have slowed down development immensely.&lt;/p&gt;&lt;p&gt;Point #3 is just annoying. &lt;br/&gt;&lt;/p&gt;&lt;p&gt;Unable to get what we wanted from Apple’s iTunes sandbox environment, we decided to roll our own mock service which simulates IAP subscriptions. Our requirements were pretty straightforward. We wanted a service that:&lt;br/&gt;&lt;/p&gt;&lt;p&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;would mimic responses we’d receive from the iTunes store&lt;/li&gt;&lt;li&gt;was isolated from our codebase and behave as a 3rd party service&lt;/li&gt;&lt;li&gt;could simulate IAP transactions without an iOS device&lt;/li&gt;&lt;li&gt;could simulate different IAP subscriptions states (refunded, cancelled, etc.)&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;&lt;p&gt;So we built it!&lt;/p&gt;&lt;p&gt;The end result is a service that any of our engineers could spin up at a moment’s notice, create mock subscriptions, test weird edge cases, and then shut it all down with a few keystrokes. &lt;br/&gt;&lt;/p&gt;&lt;p&gt;If you’re interested in trying out our service, simply grab the code from our github repo: &lt;a href="https://github.com/meetup/apple-of-my-iap" target="_blank"&gt;https://github.com/meetup/apple-of-my-iap&lt;/a&gt; and take it for a spin!&lt;/p&gt;&lt;figure class="tmblr-full" data-orig-height="300" data-orig-width="400"&gt;&lt;img src="http://38.media.tumblr.com/2e811dd69377aef7d5b4fa27c00577bb/tumblr_inline_ntr339lDy61r7wad1_500.gif" data-orig-height="300" data-orig-width="400"/&gt;&lt;/figure&gt;</description><link>http://making.meetup.com/post/127718510507</link><guid>http://making.meetup.com/post/127718510507</guid><pubDate>Thu, 27 Aug 2015 11:48:01 -0400</pubDate><dc:creator>confoundead</dc:creator></item><item><title>All future support tickets to be written in iambic pentameter</title><description>&lt;p&gt;Senior Machine Learning Engineer &lt;a href="https://twitter.com/estola" target="_blank"&gt;Evan Estola&lt;/a&gt; recently filed a support ticket&amp;hellip; in iambic pentameter. Our systems team promptly upgraded his workstation.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;It is in the nature of my job here, that my poor dev box is frequently down,
overloaded as it were by process - whom by their own nature and not of fault,
are very intensive of memory.

I enjoy engaging primarily - in the habit of a linux machine, and because I
sometimes do rely on (e.g. data which I need to spy on) complex data
visualizations, a racked machine would be less than ideal.

Therefore the idyllic setup for me (and lo I know that this demand be bold, and
understand if it unlikely be); a workstation under my desk as now,
but with 64G of memory.
&lt;/code&gt;&lt;/pre&gt;</description><link>http://making.meetup.com/post/125928516185</link><guid>http://making.meetup.com/post/125928516185</guid><pubDate>Wed, 05 Aug 2015 10:00:10 -0400</pubDate><category>iambic pentameter</category><category>software</category><category>meetup</category><category>bigdata</category><dc:creator>adrianparsons</dc:creator></item><item><title>Python 2.x and Unicode: A Story of Love and (almost) Murder</title><description>&lt;p&gt;&lt;em&gt;by Anne DeCusatis, Core Engineer on Meetup&amp;rsquo;s International/Partnerships team&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I love working
with Unicode. It&amp;rsquo;s fascinating how we&amp;rsquo;ve designed complex ways to represent something as simple and fundamental
as characters. It also allows us to support the incredible diversity in languages of computer users. It appeals to my
engineering brain as an interesting problem and to my more emotional
side, since having Unicode support helps people express themselves on
our platform.&lt;/p&gt;

&lt;p&gt;Recently,
I worked on a bug that attempted to change my warm-and-fuzzy Unicode
feelings.&lt;/p&gt;

&lt;p&gt;At
Meetup Engineering, we use a Python script to push our code into
review. This script integrates with both Github Enterprise and Bugzilla. Recently, one of our engineers added
support to this script to pull the bug title and description from
Bugzilla into the Python script. The title and description can then be approved or edited by the
developer and sent to Github. This was a great improvement,
since copy/pasting the bug description every time was tedious and
multi-line descriptions weren&amp;rsquo;t handled well when pasting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What&amp;rsquo;s
the problem here?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I&amp;rsquo;ll
give you a hint: Python 2.x has a &lt;a href="https://docs.python.org/2/howto/unicode.html" target="_blank"&gt;surprisingly lengthy page&lt;/a&gt;
in its documentation describing how Unicode string/byte string conversions work. I found
it to be a fun read,
the first time! The tenth time, not so much.&lt;/p&gt;

&lt;p&gt;Specifically,
our problem was:&lt;/p&gt;

&lt;p&gt;Non-ASCII
Unicode characters in Bugzilla titles and descriptions were (1) not displaying properly in the terminal and (2) throwing a
UnicodeDecodeError when we tried to dump them into a JSON string to
send in an HTTP request to Github Enterprise.&lt;/p&gt;

&lt;p&gt;This
seemed pretty simple to me, since I knew that Python&amp;rsquo;s json.dumps has
a parameter you can pass in to create a Unicode string instead of a
byte string, so I agreed to take this bug on. “Python
is fun, and Unicode is interesting,” I thought. “This will
probably take about an hour, at most, and then everyone will give me
credit for having improved our dev process immeasurably, and I will
ride a wave of admiration into the sunset.”&lt;/p&gt;

&lt;p&gt;After
a day or two of messing with it, frustrated with Unicode,
Python, and myself, I worked on other projects until I was again
feeling up to the challenge. This brings us to the present day.&lt;/p&gt;

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

&lt;p&gt;A
non-Unicode tip I learned working on this: there are diminishing returns after 6pm on a Friday. Go home and look at it
Monday morning – it&amp;rsquo;s likely you&amp;rsquo;ll get the same amount of progress as continuing to work several more hours.&lt;/p&gt;

&lt;p&gt;I digress. There
were two parts to the problem: the incorrect display of characters,
and the UnicodeDecodeError when sending them out.&lt;/p&gt;

&lt;p&gt;The
incorrect display of characters was caused because, as I found, the
byte string from our MySQL Bugzilla database was not in
the format expected of a byte string by Python. To explain this, let&amp;rsquo;s go into a little detail about strings in Python, and about how a computer stores characters.&lt;/p&gt;

&lt;p&gt;Python 2.x stores two kinds of strings: Unicode strings and byte strings. Byte strings are meant to hold ASCII characters, which always take up one byte, and can be coerced into holding other Unicode characters. Python reads and displays byte strings one byte at a time. This is bad for displaying Unicode because Unicode characters may take up more than one byte per character. Unicode strings, on the other hand, are more suitable for printing Unicode characters, since Python knows that one byte of a Unicode string might not be one character. The conversion between these types of strings is supposed to be relatively seamless.
I found that the character I was
testing with, »
(RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK), was represented
in the byte string that came from our Bugzilla database as “\xbb”, whereas a new string created with »
would be encoded as “\xc2\xbb”. This meant that I would see a question mark block (�, the computer-universal symbol for &amp;ldquo;I don&amp;rsquo;t know what this character is&amp;rdquo;) when printing the string instead of a right guillemet.&lt;/p&gt;

&lt;p&gt;To solve this, I took a round
trip. Character by character, I called &lt;code&gt;unichr(ord( c )).encode(“utf-8”)&lt;/code&gt;. In Python, &lt;code&gt;ord()&lt;/code&gt; returns the
value of a character, and &lt;code&gt;unichr()&lt;/code&gt; returns the character from a
value. Since I wanted to print a Unicode string, I also called
&lt;code&gt;.decode(“utf-8”)&lt;/code&gt; on it. My code eventually looked like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;#A workaround for 47510
def bugz_unicode(bugz_output_string):
    return u"".join([process_char(c) for c in bugz_output_string])

def process_char(c):
   return unichr(ord(c)).encode("utf-8").decode("utf-8")
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;What
can I say? It does the job.&lt;/p&gt;

&lt;p&gt;Great,
now I can dump a JSON string, right? Well, not exactly. It turns out,
urllib2 (with which we were sending the HTTP request to our Github
Enterprise instance) only takes byte strings, and I just went through
a lot of work to ensure I had a Unicode string.&lt;/p&gt;

&lt;p&gt;This
was solved by calling &lt;code&gt;.encode(”utf-8″)&lt;/code&gt; again, once I figured out that was what
I needed to do&amp;hellip;
&lt;img src="http://40.media.tumblr.com/8d438559d5917309caa5d205176b3d09/tumblr_inline_ns7wseP5Gl1tzbr9n_540.png" alt="image"/&gt;&lt;/p&gt;

&lt;p&gt;One
more note: I could only test this when my working directory was clean
of changes, because the script prevents you from pushing to git if
you haven&amp;rsquo;t committed yet. This led to some fun commit logs:
&lt;img src="http://41.media.tumblr.com/a61cd8f747d5d8a1cbb039eb92b8862e/tumblr_inline_ns7wtlnwBo1tzbr9n_540.png" alt="image"/&gt;&lt;/p&gt;

&lt;p&gt;Eventually,
though, it got merged, and is now launched and propagating its way to
all the other developers&amp;rsquo; environments.&lt;/p&gt;

&lt;p&gt;&lt;img src="http://40.media.tumblr.com/a1c3fd8b102f3d38ccb4a708f1b873d9/tumblr_inline_ns7wuuWXNK1tzbr9n_540.png" alt="image"/&gt;&lt;/p&gt;

&lt;p&gt;Note
that there were, in the end, 45 commits, but only 20 lines changed.
In other words: I wrote over twice as many lines &lt;strong&gt;about&lt;/strong&gt;
the problem as I did &lt;strong&gt;to fix&lt;/strong&gt;
the problem.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In
conclusion&amp;hellip;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;1.
Python 2.x + Unicode = CAUTION&lt;/p&gt;

&lt;p&gt;2.
We use &lt;a href="https://en.wikipedia.org/wiki/Jython" target="_blank"&gt;Jython&lt;/a&gt;, so we&amp;rsquo;ll likely be on Python 2.x
for a while.&lt;/p&gt;

&lt;p&gt;3.
I still love Unicode, and I still love Python. But I no longer feel
that they could be two great tastes that taste great together.&lt;/p&gt;

&lt;p&gt;&lt;img src="http://40.media.tumblr.com/ad3ab386d2aa31b37b3c4cd3dc7763d1/tumblr_inline_ns975hk0x41tzbr9n_540.png" alt="image"/&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://twitter.com/honest_update/status/625659846285627392" target="_blank"&gt;via https://twitter.com/honest_update/status/625659846285627392&lt;/a&gt;&lt;/p&gt;</description><link>http://making.meetup.com/post/125429906409</link><guid>http://making.meetup.com/post/125429906409</guid><pubDate>Thu, 30 Jul 2015 10:00:13 -0400</pubDate><category>python</category><category>unicode</category><dc:creator>anne-meetup</dc:creator></item><item><title>Our engineers have been doing a lot of i18n...</title><description>&lt;img src="http://36.media.tumblr.com/4373c7be41e54b3076759119c9d5ea03/tumblr_nrsv5wuyur1qerlgao1_500.png"/&gt;&lt;br/&gt;&lt;br/&gt;&lt;p&gt;Our engineers have been doing a lot of i18n (internationalization) work to make Meetup more friendly around the globe!&lt;/p&gt;

&lt;p&gt;This past spring, we launched changes to how our pages are indexed by search engines. You can now access Meetup from links which correspond to different languages - for instance:&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;&lt;a target="_blank" href="http://www.meetup.com/de/"&gt;http://www.meetup.com/de/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a target="_blank" href="http://www.meetup.com/es/"&gt;http://www.meetup.com/es/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a target="_blank" href="http://www.meetup.com/fr/"&gt;http://www.meetup.com/fr/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a target="_blank" href="http://www.meetup.com/it/"&gt;http://www.meetup.com/it/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a target="_blank" href="http://www.meetup.com/pt/"&gt;http://www.meetup.com/pt/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;(you must be logged out, otherwise the page is rendered according to the language in your account settings)&lt;/p&gt;

&lt;p&gt;In April we changed our &lt;a target="_blank" href="http://www.meetup.com/sitemap.xml"&gt;sitemap&lt;/a&gt; to surface these URLs to search engines. Now, Meetup will be presented in search results in users’ local languages. Not only are people registering more often this way, they are getting a better, more native experience!&lt;/p&gt;</description><link>http://making.meetup.com/post/124594479437</link><guid>http://making.meetup.com/post/124594479437</guid><pubDate>Mon, 20 Jul 2015 15:13:47 -0400</pubDate><category>seo</category><category>meetup</category><category>i18n</category><category>hockeystick</category><category>global</category><dc:creator>jakemeetup</dc:creator></item><item><title>Steps Towards Automated Testing with Docker and Jenkins</title><description>&lt;p&gt;&lt;i&gt;blog post written by Jose Rodriguez,&lt;br/&gt;Product Engineering Lead at Meetup&lt;/i&gt;&lt;/p&gt;

&lt;h2&gt;Going from Play-Doh to Legos&lt;/h2&gt;

&lt;p&gt;We’re making an effort to clean up our core codebase. Most of Meetup’s architecture has been monolithic for a long time with a lot of our core code shared in the same scope of a singular webapp. We established no rules or boundaries for how our codebase should interface with other parts. Like a ball of mixed Play-Doh, our code can be hard to identify with no separation of concern. Pieces call other pieces in ad-hoc fashion, resulting in tightly coupled code that is both difficult to test and difficult to maintain.&lt;/p&gt;

&lt;p&gt;Instead, we want our code to be like Legos. We want to build small identifiable components with defined interfaces of interaction. With the implementation of code reviews and some good luck in hiring talented engineers, Meetup’s code quality has improved over the past few years. Now we’re interested in enforcing constraints to keep our codebase modular, decoupled, and testable.&lt;/p&gt;

&lt;p&gt;&lt;img src="https://s3.amazonaws.com/making-meetup-images/play-doh.png" alt="Play-Doh code vs Lego code."/&gt;&lt;/p&gt;

&lt;p&gt;Meetup is older than most people realize.  Just a few weeks ago we turned 13.  While it’s exciting that we’ve lasted this long, it also means we’ve accumulated a lot of code over the years.  The truth is that most of our code isn’t tested in an automated way.  Much of it isn’t even testable.  We have sustained this long by investing in a manual testing strategy.  If we took a holistic view of Meetup’s testing coverage it might look something like this:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://s3.amazonaws.com/making-meetup-images/pyramid.png" alt="Healthy testing vs our testing, just being honest."/&gt;&lt;/p&gt;

&lt;p&gt;We know what a healthy testing population looks like and we’re far from it. Last year we took the first step by transitioning from a giant source tree, all in the same scope, to a hierarchy of modules. We even have goals set around the long term vision of those modules. The transition was a success and now we’re ready for the next step, to bring automated unit testing to Meetup with the help of Docker and Jenkins.&lt;/p&gt;

&lt;h2&gt;What have we learned from the past?&lt;/h2&gt;

&lt;p&gt;We’ve tried something similar before. Our systems team recognized the importance of testing and attempted to setup a continuous testing infrastructure. So why didn’t it work?&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Tests depended on external services like MySQL, Redis, or Memcached.&lt;/strong&gt;&lt;br/&gt;
We skipped any form of unit testing and went straight for integration. An isolated environment was required but our implementation was not reproducible. Since we use a shared development model at Meetup, two engineers running the test suite would end up in state conflict if run at the same time.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Engineers weren’t familiar with our continuous testing infrastructure.&lt;/strong&gt;&lt;br/&gt;
There was only one instance of Jenkins running. With no alternative to poke and prod, engineers were hesitant to learn more about it. Engineers are curious, but less so when it may mean downing a running service.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;An absence of gatekeeping for test regressions.&lt;/strong&gt;&lt;br/&gt;
If an engineer broke an existing test no one was informed until after that code was merged to master. Bugs were created to fix broken tests but by the time it had been addressed, the tests were no longer salvageable or relevant.&lt;/li&gt;
&lt;/ul&gt;&lt;h2&gt;Answering these problems with Docker and Jenkins&lt;/h2&gt;

&lt;p&gt;In the ladder to automated testing, the first step is unit tests. Integration tests are useful but harder to do. Unit testing is fast and easy. It gives us a solid foundation to start influencing design and prevent regressions. Once we have a solid unit testing foundation, then we can start moving towards higher forms of automated testing.&lt;/p&gt;

&lt;p&gt;We need an automated testing setup that is reliable, reproducible and which prevents future regressions and breakage. In evaluating alternatives, we found that Jenkins with Docker would meet this criteria. Around this time we were introduced to the approaches that eBay had been trying with containerizing Jenkins slaves. You can find more about it here: &lt;a href="http://www.ebaytechblog.com/2014/05/12/delivering-ebays-ci-solution-with-apache-mesos-part-ii/" target="_blank"&gt;http://www.ebaytechblog.com/2014/05/12/delivering-ebays-ci-solution-with-apache-mesos-part-ii/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We started with their second approach of mounting a slave container’s Docker sock inside of itself. The slave now has access to run other containers without the overhead of Docker in Docker.&lt;/p&gt;

&lt;p&gt;&lt;img src="https://s3.amazonaws.com/making-meetup-images/jenkins-slave2.png" alt="Docker slave container running with access to its own Docker sock."/&gt;&lt;/p&gt;

&lt;h3&gt;Reliable builds regardless of environment&lt;/h3&gt;

&lt;p&gt;When introducing new infrastructure, reliability influences adoption. If the thing is a piece of junk, no one will want to back it. We needed a method to reproduce builds over and over from scratch with no environmental side effects. By running builds and tests inside of a container, we have detailed control over these side effects. Docker gives us confidence in these environments and an easy interface for working with them.&lt;/p&gt;

&lt;p&gt;&lt;img src="https://s3.amazonaws.com/making-meetup-images/jenkins-build2.png" alt="The Jenkins job waits on the status of the build container to determine failure/success."/&gt;&lt;/p&gt;

&lt;h3&gt;Creating a reproducible CI environment&lt;/h3&gt;

&lt;p&gt;To increase adoption with the continuous integration (CI) tooling, we wanted the tools and environment to be reproducible. The singular Jenkins instance didn’t work for us in the past. Our engineers should be able to reproduce the same configuration in their own environment, just like any production service.&lt;/p&gt;

&lt;p&gt;We took eBay’s approach a step further by turning the Jenkins master into a container as well. We have a base Docker image with shared plugins and Jenkins version. We then extend the base image with detailed configurations for a particular Jenkins master. Now engineers can reproduce any configuration we have available.&lt;/p&gt;

&lt;p&gt;In this reproducible environment, we wanted as much decoupling as possible. Configurations should have as little environment specific details as possible. We also wanted to create slave instances without needing configuration changes on the master. Jenkins Swarm allowed us to make starting a new slave as easy as starting a Docker container. (&lt;a href="https://wiki.jenkins-ci.org/display/JENKINS/Swarm+Plugin" target="_blank"&gt;https://wiki.jenkins-ci.org/display/JENKINS/Swarm+Plugin&lt;/a&gt;) If we want another Jenkins slave, we just run the slave container pointed at the master. The slave will use JNLP and Jenkins Swarm to register itself.&lt;/p&gt;

&lt;p&gt;&lt;img src="https://s3.amazonaws.com/making-meetup-images/jenkins-register2.png" alt="Jenkins slave automatically registers itself with master."/&gt;&lt;/p&gt;

&lt;script src="https://gist.github.com/jrodbeta/f73589e2fea39bbb0a8a.js"&gt;&lt;/script&gt;&lt;p&gt;The final result allows us to reproduce any CI setup or configuration in just a few Docker commands.&lt;/p&gt;

&lt;script src="https://gist.github.com/jrodbeta/60e280e13a00f73decc4.js"&gt;&lt;/script&gt;&lt;h3&gt;Preventing future breakage&lt;/h3&gt;

&lt;p&gt;Keeping engineers informed of breakage is good, but we also need to prevent it from entering our master branch. At Meetup, we’ve adopted GitHub flow with our core codebase using GitHub Enterprise. One of the features of GitHub we actively use are the Pull Requests (PR) to review code before it’s submitted for release. GitHub also comes with a feature to flag commits with statuses. In this case, we can use Jenkins’ GitHub Pull Request Builder Plugin to build PRs and their updates. (&lt;a href="https://wiki.jenkins-ci.org/display/JENKINS/GitHub+pull+request+builder+plugin" target="_blank"&gt;https://wiki.jenkins-ci.org/display/JENKINS/GitHub+pull+request+builder+plugin&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;img src="https://s3.amazonaws.com/making-meetup-images/github-success.png" alt="GitHub showing a success commit status"/&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="https://s3.amazonaws.com/making-meetup-images/github-failure.png" alt="GitHub showing a failure commit status"/&gt;&lt;/p&gt;

&lt;p&gt;Test breakage is now reported to both the submitting and reviewing engineers. A failing commit will alert the PR submitter, and the failed state of the PR alerts the reviewer that a problem exists.&lt;/p&gt;

&lt;h2&gt;Conclusion and what’s next?&lt;/h2&gt;

&lt;p&gt;We now have a reliable mechanism for determining failure. Engineers do not have to worry about false negatives due to infrastructure failure. Anyone interested in updating our infrastructure can test changes in their own CI setup. And finally, we have gates in place to prevent test breakage from going unnoticed.&lt;/p&gt;

&lt;p&gt;Our next step is to create more isolation in pursuit of testable integration. We’re excited about the capabilities Docker offers us here. We’re hoping that the steps we’ve taken for unit testing will help influence our code design and move us towards a more testable future.&lt;/p&gt;</description><link>http://making.meetup.com/post/122890386432</link><guid>http://making.meetup.com/post/122890386432</guid><pubDate>Tue, 30 Jun 2015 18:56:03 -0400</pubDate><dc:creator>jrodbeta</dc:creator></item><item><title>adrianparsons:For the most recent internal hackathon at Meetup,...</title><description>&lt;img src="http://41.media.tumblr.com/ecb74df1cd0dbf828e11bfb5415e397d/tumblr_nj0d1f9zNP1qfe49bo2_500.jpg"/&gt;&lt;br/&gt; &lt;br/&gt;&lt;img src="http://41.media.tumblr.com/636a9f1bc4c41fdbf397cda957249ec5/tumblr_nj0d1f9zNP1qfe49bo3_500.png"/&gt;&lt;br/&gt; &lt;br/&gt;&lt;p&gt;&lt;a href="http://blog.adrianparsons.com/post/109711395601/for-the-most-recent-internal-hackathon-at-meetup" class="tumblr_blog" target="_blank"&gt;adrianparsons&lt;/a&gt;:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;For the most recent internal hackathon at Meetup, &lt;a href="https://twitter.com/estola" target="_blank"&gt;Evan Estola&lt;/a&gt; and I built a Party Mode toggle switch. Obviously, the switch turns Party Mode on and off. Party mode activates an animated CSS disco ball on meetup.com and on &lt;a href="http://www.partymode.club" target="_blank"&gt;partymode.club&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Built with: &lt;/p&gt;&lt;p&gt;Mason jar (we live in Brooklyn, okay?)&lt;br/&gt;Spark Core&lt;br/&gt;USB charger&lt;br/&gt;Toggle switch (from the hardware store across the street)&lt;/p&gt;&lt;/blockquote&gt;</description><link>http://making.meetup.com/post/109884865912</link><guid>http://making.meetup.com/post/109884865912</guid><pubDate>Mon, 02 Feb 2015 11:21:43 -0500</pubDate><dc:creator>adrianparsons</dc:creator></item><item><title>Tech stands up for the little guy at Seattle Interactive Conference</title><description>&lt;p id="docs-internal-guid-09aefb35-539c-5f01-71a7-d98951be3887"&gt;&lt;span&gt;Earlier this month I was fortunate enough to attend the &lt;a href="http://www.seattleinteractive.com/" target="_blank"&gt;Seattle Interactive Conference&lt;/a&gt;, a two-day event “celebrating the convergence of online technology, creativity, and emerging trends in one of the world’s most creative cities.” Traditionally known as home base for two of the &lt;a href="http://www.microsoft.com/" target="_blank"&gt;world’s&lt;/a&gt; tech &lt;a href="http://www.amazon.com/" target="_blank"&gt;giants&lt;/a&gt;, Seattle now plays host to a multitude of successful online startups, many of whom were represented at this year’s event. I learned a number of concepts I’m excited to bring back to NYC, but one thing I found particularly exhilarating is how tech continues to find new ways to empower those in need of agency or representation, which is something close to our hearts here at Meetup.&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span&gt;The first session I attended at the conference, a talk by &lt;a href="http://crowdcompanies.com/" target="_blank"&gt;Crowd Companies&lt;/a&gt; founder &lt;a href="https://twitter.com/jowyang" target="_blank"&gt;Jeremiah Owyang&lt;/a&gt; on how new companies are increasingly turning to crowd-based platforms as a basis for their business models, highlighted plenty of instances of the trend. &lt;a href="https://www.uber.com/" target="_blank"&gt;Uber &lt;/a&gt;and &lt;a href="https://www.airbnb.com/" target="_blank"&gt;Airbnb&lt;/a&gt; were mentioned (as they were in almost every session of the conference, at one point a speaker asked how many people in the room had taken an Uber to the conference that day and at least half the room indicated they had). But Owyang also highlighted plenty of little-guy-empowering startups that haven’t broken into billion-dollar-valuation visibility. &lt;a href="http://leftoverswap.com/" target="_blank"&gt;LeftoverSwap&lt;/a&gt;&lt;/span&gt;&lt;span&gt; helps connect people with extra prepared food with those looking for a bite to eat, helping reduce food waste and making locally-available food more affordable. &lt;a href="https://boatbound.co/" target="_blank"&gt;Boatbound&lt;/a&gt;&lt;/span&gt;&lt;span&gt; takes the concept of vehicle- and ride-sharing and moves it offshore. &lt;a href="http://helparound.co/" target="_blank"&gt;Helparound&lt;/a&gt;&lt;/span&gt;&lt;span&gt; connects diabetics, so that if someone finds they are stuck without a glucose meter or other supplies, they can send a call for help to anyone in the area who might be able to provide them help in a pinch. Here at Meetup, we’re also doing what we can to build the communities of &lt;a href="http://community-food-swap.meetup.com/" target="_blank"&gt;food swappers&lt;/a&gt;&lt;/span&gt;&lt;span&gt;, &lt;a href="http://boating.meetup.com/" target="_blank"&gt;boat sharers&lt;/a&gt;&lt;/span&gt;&lt;span&gt;, and &lt;a href="http://diabetes.meetup.com/" target="_blank"&gt;diabetes support&lt;/a&gt;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://twitter.com/JanelleMaiocco" target="_blank"&gt;&lt;span&gt;Janelle Maiocco&lt;/span&gt;&lt;/a&gt;&lt;span&gt;, CEO of &lt;a href="https://www.farmstr.com/" target="_blank"&gt;Farmstr&lt;/a&gt;&lt;/span&gt;&lt;span&gt; also hosted a fantastic session about the work Farmstr does to connect small farmers to sell directly to local produce buyers. This benefits farmers by eliminating a lot of guesswork farmers normally need to do in terms of how to bring to market, and saves them a lot of time because they are delivering to a specific place at a specific time instead of spending all day at market. She also spoke on the unfortunate statistic that only 5% of tech startups are founded by women. &lt;a href="http://local-food.meetup.com/" target="_blank"&gt;Local food&lt;/a&gt; and &lt;a href="http://women-entrepreneurs.meetup.com/" target="_blank"&gt;female entrepreneurship&lt;/a&gt; are two more topics that we here at Meetup hope to support and help build communities around.&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span&gt;As a side note, one other session at the conference had a bit less to do with tech supporting the little guy, but resonated with me as a Meetup employee nonetheless. It was given by Briar Waterman, the Director of Digital Creative at Starbucks. He touched on the company’s &lt;/span&gt;&lt;em&gt;Meet Me At Starbucks&lt;/em&gt;&lt;span&gt;, a campaign that sends filmmakers to Starbucks locations around the world to film interactions between communities meeting within, then publishes the footage. One such highlighted community was a Meetup Group, &lt;a href="http://www.meetup.com/nystorytellers/" target="_blank"&gt;The Long Beach Scrapbooking and Digital Photo Group&lt;/a&gt;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;iframe frameborder="0" height="315" src="//www.youtube.com/embed/Glnz9gyEZ9A" width="560"&gt;&lt;/iframe&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;br/&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;br/&gt;&lt;span&gt;The videos serve to support Starbucks’ assertion that “good things happen when we get together.” Which, to me, sounds just a tad bit familiar.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;A big thanks to all the organizers, speakers, and participants at Seattle Interactive for an educational, eye-opening, and supremely enjoyable two days.&lt;br/&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;strong&gt;Duncan is a QA Engineer at Meetup who thinks the Pacific Northwest is the Pacific North&lt;em&gt;best&lt;/em&gt;&lt;br/&gt;&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;</description><link>http://making.meetup.com/post/101906524955</link><guid>http://making.meetup.com/post/101906524955</guid><pubDate>Thu, 06 Nov 2014 00:30:16 -0500</pubDate><dc:creator>duncanmeetup</dc:creator></item><item><title>Meetup Hacks: Introducing Meetloaf! </title><description>&lt;p&gt;&lt;em&gt;Each quarter, Meetup holds a two-day internal hackathon. Engineers work on hacks ranging from launchable features, to internal tools, to the downright ridiculous. &lt;/em&gt;Meetup Hacks &lt;em&gt;is a new feature focusing on these projects.  &lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;em&gt;Caroline is a Core Software Engineer who recently graduated from Tufts University. When she’s not hacking, she listens to last.fm while working on Meetup notifications.&lt;/em&gt; &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Being new on the job and wanting to contribute with something practical, I was all set to build something that might actually get pushed to production.  I spent the week before the hackathon tossing around a lot of ideas, mostly relating to a big project I’m about to start.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Then, as I was sitting at my desk blasting some tunes and hunkering down on a tough bug, an incredibly random idea for a project came to me: what if I allowed our members to hook up to their last.fm accounts?  &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;I&amp;rsquo;m the kind of person who loves data, and loves music, so when it comes to a combination of those two things, I swoon, just a little. I put feelers out into our dev hipchat channel to see if there were any other last.fm users around so that I could possibly justify doing this hack, but I only got two responses. Turns out, most people ditched last.fm a few years ago.  &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;But, the more I talked to my friends about it, the more I wanted to do it.  It&amp;rsquo;d be something fun and I&amp;rsquo;d get to learn about the authentication process. &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;And come, on what’s better than music? &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Answer: nothing. &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;When we got around to presenting our ideas at the hackathon kickoff, it turned out that two of our Data Team engineers (Brian and Evan) had a similar idea.  But, they wanted to use the Facebook “Like” data that we have from some of our members to generate a concept of the music taste profile for a group.  At that point, I was pretty set on last.fm, so we decided to get together at the end of day 1 and see what we could do about combining our projects.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Hack&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;img alt="image" src="http://33.media.tumblr.com/b6f40bd5b5892737a7fd7738027a132b/tumblr_inline_nbozqjwbsP1s5hqln.png"/&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;span&gt;So, then it was time for me to dig into the &lt;/span&gt;&lt;a href="http://www.last.fm/api" target="_blank"&gt;&lt;span&gt;last.fm api&lt;/span&gt;&lt;/a&gt;&lt;span&gt;.  I explored a little bit and implemented calls to a few of the artist specific methods just to get comfortable before diving into user auth.  &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;While the api is great on the whole, the one thing that might be frustrating to some developers is that they&amp;rsquo;ve developed their own authentication system. It&amp;rsquo;s incredibly similar to OAuth, but not enough so that I could use the framework that Meetup already has for linking to OAuth services like LinkedIn and Facebook. Some would groan, but I know next to nothing about authentication protocols, so I took this as an opportunity to get familiar with them.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;There are a few different ways to authenticate with last.fm, but I chose the desktop application route so that I wouldn&amp;rsquo;t have to allow last.fm to make calls to our development environment.  From there, it&amp;rsquo;s a relatively simple process: you get a token from last.fm, have the user request authorization using that token, and then you can fetch a session (and corresponding key) with last.fm. The somewhat difficult part comes from deciding how to store intermediary data (like the token) and how to proceed through all the stages I mentioned.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Because this was a hackathon, I took the hacky way out, and passed the token through the whole process, and kept track of the user&amp;rsquo;s state by monitoring the window I had opened for them to authenticate with last.fm (so that I would know when to trigger a request for a session key). Kids, don&amp;rsquo;t try this in production. &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Despite all the hackiness, I got to learn something: for the first time, I got to play with our internal Ajax method library&lt;/span&gt;&lt;span&gt; in the process to make the links between what was happening in the browser and storing the account tie in the DB.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Once I had authentication, I could actually start doing fun things with user data.  As per Murphy’s law, the api call I was hoping to use was deprecated, and ended doing a bunch of fun set operations to develop a playlist for a meetup group based of the last.fm stylings of anyone authenticated in that group. Everything was fine and dandy, until I realized that I only had two Meetup HQers authenticated, and didn&amp;rsquo;t have enough data to make good playlists.  &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Luckily, Brian and Evan, who were working on the facebook side of things, let me dig into some of what they had built and I got to embark on the process of using the &lt;/span&gt;&lt;a href="http://developer.echonest.com/docs/v4" target="_blank"&gt;&lt;span&gt;echonest api&lt;/span&gt;&lt;/a&gt;&lt;span&gt; to convert between facebook&amp;rsquo;s identifier and musicbrainz ids, which is what last.fm uses.  So, after all this work, anyone authenticated with last.fm could click a button to create a last.fm playlist for any group they were in.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Combining Forces&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Finally, I got together with Brian and Evan to set up the beautiful integration of our projects.  They had pulled all the facebook info and associated pairs of meetup groups and music artists with a numeric score. I supplied them with a way to look up the music taste for a member (since last.fm is more detailed than facebook on a per member basis), and Evan adjusted his beautiful group recommendation algorithm to recommend meetup groups to a member based on their music preferences.  While the recommended groups were not ones that I might be interested in, it definitely knew me. I got recommended a couple Meetups in Urbana, Illinois (thanks, Elsinore), and though I was unaware that my music taste so heavily reflected my gender, I was recommended a lot of female focused groups. &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;While I was right in my assessment that this is not going to make it into production, I had a great time, got to know some other MEME teamers a little bit better, and most importantly, I now have &lt;/span&gt;&lt;a href="http://www.last.fm/user/cmarcksthespot/library/playlists/6yfly_meetup_hq" target="_blank"&gt;&lt;span&gt;a playlist&lt;/span&gt;&lt;/a&gt;&lt;span&gt; on last.fm reflecting our collective music taste.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong id="docs-internal-guid-08deb4fc-6039-a0c8-f826-d5a80ed1d8b4"&gt;&lt;br/&gt;&lt;span&gt;Special thanks to the guys for coming up with the ultimate name for the ultimate project: Meetloaf. &lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;</description><link>http://making.meetup.com/post/97278522598</link><guid>http://making.meetup.com/post/97278522598</guid><pubDate>Fri, 12 Sep 2014 00:30:00 -0400</pubDate><dc:creator>krafte2</dc:creator></item></channel></rss>