Carpe Daemon
Rants, reviews, and random thoughts from my world and the world of tech.
Wednesday, May 1, 2013
The Story of BTC Price Alerts
As I refreshed BitcoinCharts.com for the thousandth time in a day, a thought struck me, "I wouldn't need to waste inordinate amounts of my time refreshing this page if there were a service that would email me when bitcoin's price made a significant movement." After a couple minutes of research, I found two services claiming to have this capability, but neither seem to be very fully featured, and frankly, both were a bit sketchy. I was looking for a side project to hack on, and Andy was Looking for a side project to hack on, we decided at that point to create BTCPriceAlerts.com.
The minimum feature set was as follows:
- Allow users to sign up and authenticate with the service
- Allow users to create and configure alerts at a given price threshold
- Automatically send an email or SMS alert when the price of Bitcoin crosses the configured threshold
More than creating a functional service, the purpose of BTCPriceAlerts.com is to explore new technologies, architectures, and workflows. What else are side projects for? After a couple design sessions we settled on a far-flung architecture consisting of at least six different components. Six components is probably a little overkill for such a limited feature set, but that's the point. Additionally, breaking the app into bite size components allows each component to own it's function and allows for the addition of new features without any of the components getting overly complex. After settling on the following components, we divvied up the work and started implementing. Andy wrote the user facing app while I took care of the alert architecture which included Twilio and Sendgrid integrations, the message queue, the message workers, the alerts API, and the market data daemon.
Message Queue
We are using a cloud messaging queue that is provided as a service from Iron.io. I couldn't really be happier with the service so far. Starting with the signup process and documentation and continuing through the automated daily usage reports, the product has been phenomenal. Using IronMQ from Iron.io provides me with a lot of infrastructure that I don't need to build or maintain. Some of my favorite features are the management console and the usage reports. The management console allows me to easily see the state of all of my queues and configure them. Because the app is a notification app, an important metric is the number of notifications sent. Iron.io provides this information both in the management console and in the daily usage report email without me having to track this metric explicitly.
Maybe the coolest feature of the IronMQ message queue is the ability to turn any queue into a "push queue". This means that any message put on the queue is immediately pushed out to every "subscriber". In my case the subscribers are the messaging workers (more on the workers next). The message queue ensures reliable delivery by requeueing the message if the "push" isn't acknowledged with a success indication. This greatly reduces the likelihood that a message is missed.
Message Workers
Iron.io provides another great service called IronWorker. IronWorker is a distributed worker architecture meant for asynchronous and long running tasks. IronWorker exposes an HTTP webhook endpoint that receives the push from the message queue with a JSON payload. That payload is then made available to the worker code. In the case of BTC Price Alerts, one worker handles the SMS notifications, and another worker handles the email notifications. The SMS worker receives messages from the message queue and delivers them through Twilio while the email worker delivers emails through Sendgrid.
The process of creating the workers went smoothly thanks to the quality of the Iron.io docs. They provide a command line tool so that creating a worker is just a matter of writing the code, creating a few configuration files, and running a few command line commands. Once the worker is uploaded, all I had to do was point my push queue at the endpoint exposed by the worker.
Market Daemon
The market daemon's job is to extract pricing data from Mtgox's API and send it to the alerts API so that alerts can be triggered. Mtgox caches their API results for 30 seconds, so polling more frequently than that is futile. I originally wrote this extremely simple polling loop in Go because I was curious to see what that language was all about. I've been following along with the community a little bit, and I wanted to try to implement something. This part of the app was so simple that I figured it couldn't hurt. Mostly due to my inexperience with everything from the syntax to the build process, building this daemon was much more painful than it needed to be. I eventually decided to scrap the Go implementation and fall back to ruby and EventMachine for the polling loop. This implementation has worked much better.
User App
The user facing web app is written in Python on Django. The user app owns all user data including the email and phone number associated with each user account. Importantly, the user app does not own any of the alerts data. The usr app includes a one page app to administer all of a user's alerts. That console faciliates creating, viewing, and deleting email and SMS allerts. All alerts data is queried form the alerts API in real time when it is displayed in the console. All alert creation and deletion requests are basically proxied through the user app to the alerts API. As I mentioned, I didn't write the user app, so you'll have to wait for Andy's highly anticipated blog post to learn the juicy details.
Alerts API
The heart of the alerts architecture I created is the alerts API. Written on Sinatra with a Padrino admin interface, the app owns all of the alerts data. It exposes a standard JSON REST API for CRUD operations on alerts. The app also receives price updates from the market daemon, calculates which alerts have been triggered, and enqueues those alerts on the message queue. The alerts data itself is stored in a Heroku Postgres database. The alerts API app does not have any user facing UI--only an admin interface. The app also doesn't know anything about the actual users other than a unique identifier. This design decision was made so that the alerts API app could focus on storing alerts and calculating which alerts are triggered.
Monitoring
BTC is a "highly critical" service with many "demanding users" monitoring "millions of dollars" worth of bitcoin. Entrusted with this duty, we've set up standard monitoring services to notify us when the app goes down. To monitor the alerts API and the user app, we using Pingdom. Monitoring the market daemon is a little bit trickier. First, it doesn't expose an HTTP endpoint to ping. Second, it's a little harder to monitor when something doesn't happen (i.e. the daemon stops sending prices to the alerts API). To monitor this part of the app, we use a service called Dead Man's Snitch. Basically, we receive a notification if a certain HTTP endpoint isn't pinged for an hour straight. The market daemon just pings that endpoint every time it successfully polls the Mtgox API and sends that price to the alerts API.
BTC Price Alerts is currenly in "private beta". I've been using it for about two weeks now and have gained some insights into changes that need to be made to the product. For intance, notification rate limiting is more or less a requirement to avoid "alert blindness" in which the volume of alerts causes them to be ignored entirely. We also plan to integrate exchanges other than Mtgox for those users who trade on other exchanges. Coinbase (though not truly an exchange) is a likely candidate for the next integration.
The project has definitely fulfilled its pupose of being an entertaining side project and an experiment in architecture. Of course, feel free to try it out and let me know what you think!
P.S. All of the source code is on Github if you want to check it out. I'm 44maagnum. Andy is ajk14.
Tuesday, April 30, 2013
Grand Opening: CabralRunningAcademy.com
Donn Cabral's Olympic run this past summer was nothing short of awe-inspiring, so you can imagine how flattered I was when he contacted me to design a website for his first entrepreneurial venture as a professional athlete. Being an entrepreneur myself and a close friend, I jumped at the opportunity to help him out. The story of Cabral Running Academy itself is yet to be written, but I'm just finishing the first chapter in the story of CabralRunningAcademy.com. This is going to be a technical deep-dive into the site's technologies and architecture which will delight a certain type of reader and send most others running for the hills. I'm alright with that.
We only roughly sketched the requirements for the site before embarking--partially because I had a pretty good preexisting idea of what the site needed to do and partially because Donn and I are good friends and weren't worried about communication. Cabral Running Academy is to be a series of camps conducted by Donn and his coach Peter Oviatt. The website fulfills both a marketing and an administrative role within this pursuit. The site facilitates campers learning about and enrolling in the camps. It accepts credit card payments (with coupon discounts) and tracks camper registrations. The site includes information about each camp specifically and about the organization more broadly. Easy editing of pages by a non-technical user was also an important requirement.
I'll start and the foundation and progress upwards. The site is built using Ruby on Rails, an extremely popular web application framework. Ruby on Rails is powerful in that it allows developers to move quickly when they are performing standard tasks, but also gives them "sharp" enough tools when they need to do something specific. I could have built the site using any number of higher level tools--WordPress and drag-and-drop website builders like Weebly would have been good candidates (just because the requirements of the site weren't all that advanced). These tools probably would have gotten me 0 to 60 faster (especially with respect to editing pages), but they never would have allowed me to reach 100. Donn deserves 100.
The site needed an admin interface where Donn could see all of his registrations, edit pages, create new camps, and manage coupons. I've used a gem called ActiveAdmin in the past and found it user friendly, powerful, and easy on the eyes. Using ActiveAdmin on this project was not a difficult decision.
Many options exist for credit card processing. PayPal, Braintree, Chargify, and others have to be considered. However, I have my own darling that is still coming on the scene--a service called Stripe. Stripe is such a pleasure to work with it's almost criminal. Their APIs are the type of work that you look at and say, "Yeah, obviously that's the way it should be." Working with a product like that is, not to be too melodramatic, inspiring.
I don't claim to be a web design guru. For folks like me, I highly recommend the CSS framework Twitter Bootstrap. Bootstrap allows relatively inexperienced or untalented designers (I'll let you decide which one I consider myself) to reach a high level of design quality with minimal effort. Again, this was not a hard choice for this project.
All of these choices are fairly standard. Other standard choices made along the way were namecheap.com for domain registry, Google apps for email, Google analytics for tracking, Heroku for web app hosting, Amazon S3 for content hosting, and jQuery for javascript. The fun part comes in a few "flourishes" I added just for fun.
Perhaps the hardest requirement to really nail with the toolkit described above is the easy editing of pages. What I really needed was a content management system (CMS). This is where systems like WordPress excel. I should mention that there are libraries available for Rails that allow for CMS functionality. Most that I saw were very heavy duty and I couldn't find one that I liked. Call it hubris, but I decided that I could craft a tailored solution that would better meet Donn's needs without polluting the user interface with unnecessary functionality.
Using the same admin interface for the backend, I created a custom page editor based on the Markdown language. Markdown is specifically designed for non-technical users to produce clean web pages. Pagedown.js is a Markdown implementation in javascript. This allowed me to create a live preview of the custom page. Live previews allow for more precise and faster editing due to increased feedback. Plus, I wanted to write a live preview for kicks.
Images clearly needed to be part of the custom editing functionality. However, storing images on the web application host was not an option due to Heroku's ephemeral file system. Amazon S3 seemed like a logical choice for content hosting.
CabralRunningAcademy.com provided a lot of opportunities. The opportunity to help out a friend and fellow entrepreneur. The opportunity to try out some new technologies. And the opportunity to create something useful of which I can be proud. I think I've successfully created a pretty good out-of-the-box registration, administration, and CMS app, and I had fun doing it. Thanks, Donny!
Sunday, November 25, 2012
On Unsubscribing
Let me preface this by saying that I have no issues with Spotify as a service. I have friends who swear by it. I tried it right when it came out. I happen to prefer Pandora, but that's not a strong indictment of Spotify. All this aside, I've had an atrocious experience as a Spotify customer.
Let's start with the emails. No offense, but I Do Not Care if a high school classmate who I rarely spoke to then and probably won't ever speak to again added a track to the playlist "Listened to on the radio and liked it!" I have better things to fill up my inbox. I especially don't need to be informed of this at multiple times during my day. Ok, deep breath. There are laws about this. Any marketing email must have an operable unsubscribe method at the bottom. Further, this unsubscribe cannot require you to do anything more complex than enter your email address. I don't make the rules--I just follow them. Spotify doesn't.
I follow the link in the bottom of the email to change my "email preferences"--as if I prefer receiving any email at all. Not so fast. Log in. Don't remember your password because you haven't used the service in a year? Click here to retrieve your password. I log in and unsubscribe from all updates. The friendly little green man informs me that my changes may take up to 10 days to take effect. That makes sense. An entirely digital service running on hardware that makes millions of decisions a second is going to take 10 days to realize that I don't want email. Whatever. At least I won't get any more emails.
Wrong! 10 days go by, and I still get 1-2 emails a day about meaningless crap. Spotify, we're through. And it's not me... it's you. After wading deep into the support section to find a contact form, I inform the kind customer service rep in measured terms that--thank you but no thank you--I will not be continuing with your service. Please delete my account. I don't know why you need to know my zip code, the name of my unborn firstborn child, and my deepest darkest secrets in order to delete my account, but after what you've put me through I guess I'll do it because I can see the light at the end of the tunnel. "We're sorry to see you go. Hopefully we'll see you again some time." Don't count on it.
A few more days pass. ANOTHER EMAIL! This must be a joke. My zombie Spotify account is exacting revenge from beyond the grave. I'm done with measured responses. An unfortunate customer service agent is going to get a piece of my mind. I go to Spotify.com and find the link to the contact form. "Please log in to submit your question." This is a sick, sick joke. Spotify, why do you hate me?
Now you might be thinking snidely to yourself, "You have no idea what it's like to run a software business! Why would you allow people to easily unsubscribe from your service?" I would respond to this straw man by saying that I do know how to run a software business--and further, I've been doing just that for the past year now. If you're my client, I don't want you to subscribe simply because you can't figure out how to unsubscribe. If you aren't in love with the software I create, I don't want your money. Simple as that.
In pursuit of this goal, we've implemented a self-service mechanism for our clients at Waiter d' to activate and deactivate their account on a monthly basis. Pick a random ski resort town with highly seasonal traffic--say Park City, UT. Restaurants don't want to pay for an electronic wait list during the summer when they never have a wait. Responding to this feedback, we inserted a "Suspend Account" button into the account administration screen. This allows customers to activate and deactivate their account at their leisure. The kicker? We'll save all their data until they decide to come back! They can pick up right where they left off (we also allow all data to be easily exported in a variety of formats).
You may think that I'm making a mountain out of a mole hill here, but I think that the two approaches to unsubscribing are more than just a difference in execution--they are a difference in attitude. Taking the stance that your customers will flee if you let them doesn't seem like a successful or fulfilling way to run a business. I'd rather treat my customers with the respect they deserve--like the very lifeblood of my business. Because they are.
Saturday, October 20, 2012
The Holy war that is HTML5 vs. Native
What follows below is an email I wrote today to a smart but non-technical friend in response to a question about HTML5 vs. native app development. Yes, there are small technical inaccuracies. However, before you start a flame war, ask yourself if the errors really hinder the original goal of informing a friend about the differences between HTML5 and native apps.
=================================================
Let me preface this by saying that the HTML5 vs. Native debate is a religious one. There are zealots on both sides. Being the cool-headed and scrutinizing moderate that I fancy myself, I can see the merits of both.
=================================================
Let me preface this by saying that the HTML5 vs. Native debate is a religious one. There are zealots on both sides. Being the cool-headed and scrutinizing moderate that I fancy myself, I can see the merits of both.
The difference boils down to accessibility vs. capability.
HTML5 is much more accessible. Because it's being accessed through the browser, one can access an HTML5 app from a myriad of devices (I know I'm holding your hand a bit here but bear with me). I'll give you an example. I was selling Waiter d', our restaurant software app, the other day and I realized that the restaurant POS was running Windows. Without writing a single line of code, I downloaded Chrome (IE was already on there, but it's a piece of [redacted]) and the app was fully functional running on their POS--a piece of hardware they already own. That was a pretty cool moment.
Developing for multiple platforms is incredibly resource intensive. If you want to be legit, you have to have some combination of an iOS team, an Android team, a Windows team, a Mac OSX team, a Blackberry team (not for long, but for a long time this was essential for enterprise apps), and possibly another team or two depending on your target market. Each of these platforms require their own experts. This means that the most senior member of the iOS team wouldn't even be able to get hired on the Windows team. There just isn't as much overlap as you would think.
This is a digression, but I want to tell you a little story. When the Internet was invented, it couldn't do [redacted]. It could serve static webpages and the browsers could sometimes render them correctly. The Internet is only as powerful as it's end clients (in this case the browser). Any type of information can travel over the wire, but if the browser can't interpret it, then you're S.O.L. I'll give you a very technical example and hope that I don't lose you. Cascading Style Sheets, or CSS, are what is used to make a web page appear the way that it does. Designers can only style webpages in the way that CSS allows them. A very specific example of this is rounded corners on page elements. Look at any webpage from 2001. There will be zero rounded corners. Look at every single attractive webpage today. Rounded corners everywhere. This is because a few years ago, rounded corners were added to CSS. In practical terms, this means that the people programming browsers (like the people making Chrome. I'm not talking about people writing webpages here.) taught them that when the stylesheet contains "div { border-radius: 4px; }" that means that all of the "div" elements should have corners rounded off at a 4 pixel radius. Before that point, rounded corners simply weren't an option. Sorry for the injection of code there, I couldn't help myself.
Moving on. In 2004 some people got together (they eventually joined a group called the W3C or the World Wide Web Consortium), and they said "hey, everyone is doing all of these crazy things to try and stretch HTML to make web "apps" as opposed to web "pages". It was a bit of an identity crisis for the web. People wanted to do more with it--think Facebook, Pandora, Google Docs, Asana--than it was really designed to do. The way to fix this is to say "We're going to write down a bunch of very specific rules. Then we're going to give these rules to Mozilla, Google, Microsoft, and other so that all of their browsers behave the same way when they encounter the same HTML." If this weren't the case then application developers would be writing a Chrome version, a Safari version, and an Internet Explorer version. All of the sudden we're right back in the same multi-platform quagmire we started in. Anyway, the W3C is a "standards body" that makes sure that all of the browsers work reasonably similarly. (There are still "cross-browser inconsistencies" that exist in some of the rarer cases, and they are literally the bane of a web developers' existence.) To be clear, these private companies are free to develop whatever browser they would like. The only authority the W3C has is that they have a quorum of browser makers, so if you depart from their standard, you will be the only one and your browser will be incompatible with the web pages/apps people are writing for all of the other browsers.
Getting there. The W3C has been around since the dawn of time. However, the specific action this splinter group took in 2004 was to start work on an "HTML5 standard". These are the aforementioned rules that the browser makers are supposed to follow. The important thing about HTML5 was that it was supposed to bring web app capabilities to HTML. These capabilities included things like geolocation, local storage, and websockets. Geolocation allows web developers to access the physical location of the computer (only if the end user allows it). Local storage allows web developers to store data and application files directly on the user's computer. Before HTML5, they either had to store the data on their servers or they lost it as soon as the user quit the browser. For certain use cases, this capability is essential. Finally, websockets allowed web developers to "push" information to the browser. Previously, the web developer had no access to the user once the page was downloaded. Think web chat. Not possible IN A BROWSER without websockets. All the information about what is to be displayed simply isn't available at the time the user downloads the webpage. HTML5 includes a ton of other changes to HTML, but they are similar in that they attempt to give the browser application-like abilities.
This was all a grand plan, and in my mind it largely succeeded. HTML5 is awesome to work with because you "write once and run anywhere". However, critics will tell you that HTML5 fell short of its goal--they are also right. For instance, you can't access a phone's camera from the browser. Instagram is impossible using HTML5 as it stands today. My thinking is that HTML5, which is still evolving, will slowly gain more and more of the capabilities of native apps. For instance, access to hardware like the camera is slowly being implemented. As another example, geolocation has already been implemented. If your device has GPS, then HTML5 can use it.
The difference goes deeper than just features though. Native apps also have a decided performance advantage. Native apps run much closer to the hardware. (I don't know if I'm going to lose you here.) This means that there are fewer layers of software sitting between the user and the CPU. Because of this, everything runs faster. Think of it as a game of telephone. In this case, each person in the telephone chain is a layer of software. Users talk to native apps, native apps talk to the operating system, and the operating system talks to the hardware. Whereas users have to talk to HTML5 apps which talk to the browser, the browser talks to the operating system, and the operating system talks to the hardware. On top of that, web apps often use a framework like Ruby on Rails or Django which adds another pseudo-layer between the user and the hardware. Every layer both slows things down and makes them less fluent.
The indisputable fact is that native apps have a performance advantage. However, this needs to be weighed against the resources that it takes to develop on disparate platforms. Like I said--capability vs. accessibility.
Saturday, June 23, 2012
Killing Birds with Stones
Almost before the tires chirped on the tarmac at Rome's Fiumicino airport, we were accosted by peddlers trying to sell tourists all manner of goods and services. Our first and only experience being swindled occurred before we left the airport. Curious as to why one train to the city seemed to be 8 euro and the other one 14 euro, we approached the desk labeled "Tourist Information." They explained the difference and casually mentioned that there was a shuttle bus that would take us right to our hotel for 15 euro. Sounds like a great deal. Why not? Only as we were waiting for the "next scheduled shuttle departure" did we realize that the "tourist information desk" was a "shuttle service sales office". After an hour and a half in Rome traffic, we were made painfully aware of our mistake. We had heard that Rome was expensive, but the British couple sharing the shuttle with us put it best when they said they felt like they were "spewing euros."
Quick to recover, we caught a bus to the Trastevere neighborhood which is known for its restaurants and night life. There we met up with a teammate of mine, Tommaso, for the first of many long leisurely meals. The Italians have a well deserved reputation for culinary excellence.
First of many good recommendations |
After dinner, as is traditional, we went to play beach volleyball. Actually, we ended up playing beach volleyball entirely due to a miscommunication. We thought we were going to watch beach volleyball but ended up playing it instead. We played versus Tommaso's friends (America vs. The World). Naturally, America came out on top.
Watch out, Park City |
At just after midnight, Tommaso convinced us that the night was young, and we took off to sample the nightlife. Even though we had only been on the ground for six hours, everyone we met seemed to be outraged that we hadn't seen the Colosseum yet.
Great guy |
The next morning we met up with Tommaso and friends for some coffee (apparently when you order "coffee" they give you espresso). They gave us some advice on what to see, and we began wandering around the city.
Il Vittoriano |
We got lunch with Tommaso at another restaurant he recommended--this time in the Jewish ghetto. If the first try hadn't convinced us, this meal taught us to always follow Tommaso's recommendations.
Walking around the center of Rome, one can't help but stumble upon thousand year old ruins. It seems that on every street block there is an excavation site with surprisingly intact columns and walls.
Random ruins |
Colosseo |
Tour guide Tommaso is going to kill me, but I don't remember the name of this fountain |
The next morning we geared up for an aggressive day of sightseeing. On the agenda: the Roman forum, Vatican City, and a ruined Roman port town. We started in the forum which is an impressive set of ruins remaining from the heart of ancient Rome. A short subway ride later, and we were at the Vatican. Ten seconds after stepping off the subway, we were stormed by tour guides offering for us to "skip the line" and take their tour for 35 euro. Before long, we realized there were so many that we didn't even have time to politely decline their offers--we just had to ignore them. As we progressed through the day, the only line we waited in was a security line--which I'm pretty sure you're not allowed to skip.
We reached St. Peter's Basilica, and I was struck with a strange feeling of deja vu for having seen the place so many times in movies, on TV, and in pictures. It was odd to be seeing it in person for the first time. As soon as we stepped inside, I was almost moved to tears it was so beautiful. The basilica itself is a gigantic open span with every single surface covered in ornate decoration. The walls host paintings and sculpture. The floor is patterned with marble. And the ceiling is encrusted in gold accents. Words are wholely inadequate to describe the impression it makes.
One of many altars |
We progressed through the Vatican Museum which was unfairly overshadowed by St. Peter's Basilica on the front end and the Sistine Chapel on the back end.
After finishing our tour of Vatican City, we took a train south to Ostia Antica which is an entire town of Roman ruins. The amphitheater was still in usable condition--almost 2000 years after it was built.
Sorry for crashing your wedding |
Tommaso told us in no uncertain terms that we wouldn't get into the club dressed like Americans, so we did our best to dress in local garb. We failed.
Strut |
With only a few sites left on our bucket list, we took a mental health day and slept in until 2 PM. We did manage to see the Pantheon, Trevi Fountain, and the Spanish steps. We also learned the imporant lesson that when walking by a church, even if it looks nondescript, you should go inside. Time and again we were blown away by churches with stunning interiors that didn't look like anything special on the outside.
Average Roman church |
We ate a nice long dinner at a resaurant where wine was literally cheaper than coca-cola.
In the morning, we woke up and got some (what else?) pizza. We wandered around a bit waiting for our train to depart. We found the American embassy and said our hellos then headed to the train station. So long, Rome.
Thursday, June 21, 2012
Prague
We arrived in Prague on a train from Dresden at midnight, and we were met with an immediate display of Czech hospitality. A grungy disheveled man with a 5 o'clock shadow and slightly fewer than the normal allotment of teeth offered in broken english to take us to his hostel. While I'm sure he offers nothing but the most comfortable of rooms and model service, we had to decline his offer due to the fact that we had already booked a hotel.
With no money and no inkling as to the exchange rate, we knew our chances of buying a metro ticket were slim. We eventually found the ticket machine and who was there to help us? None other than the friendly hostel owner. We found a foreign exchange shop, and though it was closed, the prices were still published on the sign. As we haggled with the hostel owner over how many koruna we were to buy and at what price, a second helpful Prague resident shuffled over. He proferred metro tickets and offered to sell them at half the normal price. We just can't go anywhere in this country without somebody offering to help us out! The first scammer started yelling something in Czech, and the two began to argue over whose Americans these were to scam. We obtained just enough money to buy our tickets (from the machine) and get out of the station as quickly as possible. Koruna bought: 400. Scammers averted: 2. Lost travellers: 2.
After getting off at our metro stop, we still had no clue how to reach our hotel. Czech city planners didn't see fit to put street signs on each corner. Instead, they have street signs every few blocks pointing in the direction one would go to REACH a street instead of the direction the street travels.
Helpful street sign |
Needless to say, we were a little frustrated at this point and felt like sitting ducks wearing backpacks on the outskirts of Prague at 12:30 in the morning. We eventually found the hotel which was Swiss and way nicer than necessary.
We woke up to one of the most charming, beautiful cities I've ever seen. Every street is narrow, crooked, and made of cobblestones. The shops and apartments are painted brightly. The whole place seems to be from a different epoch (probably because it is).
Just charming |
I wanted to send some flowers home to Mom, but they told me that was going to be too expensive. Instead of mailing them home, I'm just going to blog them home.
Hi, Mom! |
Notice the man in the foreground telling me not to take pictures... oops |
We hiked up a small hill and came to the Prague castle--the number one attraction in Prague. The castle is an enormous complex of buildings, but by far the most impressive one was the cathedral. American cathedrals simply cannot compare to their European counterparts (I'm no expert, but I've seen a cathedral or two in my 22 years).
Step up your game, America |
We walked around the city for quite some time because the whole thing seems to be one gigantic work of art. We eventually wandered across Karluv Most which is a bridge across the main river running through the city. Along the bridge on each side are gigantic Christian sculptures along with countless street vendors.
We headed for a nice dinner on a riverboat which cost us a fraction what it would have in the US. On our way, we witnessed the remnants of the once powerful Occupy Czech movement.
Fight the good fight |
After dinner we headed for the Euro 2012 fan park in the Old Town Square. In addition to continuing our streak of the home team winning their soccer game in every city we visit, we got to see the square lit up at night.
Old Town Square |
We woke up the next morning and enjoyed some fresh local cuisine.
Eat fresh |
Turns out McDonald's in other countries have different menus. In addition to the Big Mac, this one offered the McSmazjek (don't know how to spell OR pronounce this one). The McSmazjek is a beautiful creation of which any cook would be proud. It consists of a thick slice of cheese shaped like a hamburger patty breaded and deep fried and served on a bun with a mayonnaise based sauce. In the name of trying local delicacies, I ate one. Mistake.
We walked around the middle of the city and checked out the National Museum. Being a little museumed out, we opted for the external tour only. For dinner we visited one of the top 5 microbreweries in Prague (according to God knows who on the Internet). We ate our pork knuckles, sausage, and saurkraut as workers brewed the next batch in gigantic cisterns 10ft from our table. Despite passing on several restaurants linterally named "Typical Czech Restaurant," I think we found some traditional Czech food where we went.
The brewery might have been micro, but the mugs were not |
The next morning, we walked around the center of the city, ate a little bit of food, and waited for our afternoon flight. In our ample wanderings of Prague, we found that there are no shortage of three things--money exchange shops, souveneir shops, and American fast food restaurants. One can never be more than an arm's length away from all three at the same time.
It's great to have choices |
With a half hour to kill before our train, we decided to try our luck at blackjack. We chose what seemed to be a reputable establishment and bought in.
Andy had a Happy Day |
We made our way to the airport and through minimal security to our flight. We landed in Rome with bright eyes and bushy tails--ready to carpe the diem.
Tuesday, June 12, 2012
Last Day in Berlin
Check out time was 11 AM, so we slept in just a bit, checked out, and hit the road for our final day in Berlin. Andy said a teary goodbye to the Amstel House as we left.
Tears |
Our first stop was the Olympic Stadium. Predictably, they wanted 5 Euro for us to actually go in and look around, but we contented ourselves to walk around the outside. Seeing the stadium was cool because it is huge and old and historically important.
Tiny Andy at the bottom |
We grabbed an enormous bratwurst from the vendor just outside the stadium and headed off to see a palace for which my hopes weren't too high. I ended up being blown away. The Charlottenburg Palace was an enormous building stretching what seemed like a quarter mile end to end. Behind the palace lies one of the biggest, most beautiful gardens I've ever seen (better than Mom's in the size category only, of course). The garden was so big that it seemed to be a sort of public park with locals running and walking their dogs.
Charlottenburg Palace |
Nestled in the garden were all sorts of hidden gems such as the family mausoleum.
Mausoleum |
Eerily realistic marble sculptures on sarcophagi |
We made our last stop in Berlin at the world famous Berlin Zoo. Confusingly, we seemed to be visiting on the day when only small children and their parents went to the zoo. Everyone our age must have been busy today. We saw all kinds animalistic behavior including a monkey indulging his sexual urge, two lions wrestling over a gigantic slab of meat, and elephants sucking dirt up their noses and spraying it on their foreheads. That just can't be a smart decision.
This is how I imagine Patrick if he were a monkey |
We grabbed our last kebap and headed to catch our train to Prague. Throughout our trip, the language barrier has been a definite impediment to progress. Sometimes German is intuitive--almost as easy to understand as English. Other times you wonder where all the spaces went. And sometimes you just have to give up.
This obviously means "Surfin', chattin', and mailin'" which are all reasonable things to do at an Internet cafe |
This is a museum about Andy and me |
The spacebar hasn't yet reached Germany |
I don't know what this says, but no language could possibly explain what is going on in the second panel |
Subscribe to:
Posts (Atom)