Tu Hoang parttime Ruby dev, fulltime Bun Rieu maniac

Doing business in Viet Nam

Upon writing this, I can’t help thinking back this time 7 years ago. I was a business student back then (another bedtime story), and I together with friends held a workshop on doing business in Viet Nam. Topic of the year was groupbuying, so we naturally invited a groupby executive who spent the majority of his time telling audiences how cool his business was. They ceased operations shortly after.

Fast forward to 2014, I pulled up a license just to look legitimate in the country. I still remember vividly how I had to wait 4 hours at the Department of Planning & Investment just to lodge my application. The air was humid, the ceiling fan didn’t work, the 40 meters square room was packed with sweaty businessmen in shirts and businesswomen with ridiculous makeups in skirts. I had to revisit this room for a few times mostly because the staff there wasn’t feeling particularly pleased with my application’s wording. So I smarten’d up and wore khakis and pulls from my second time, always taking a book and water with me, and somehow mastered meditation while waiting for my name to be called.

Not to mention, Charity Map being translated into Vietnamese sounded weird. I flushed a few times because people then would just stare at me for some sweet minutes because of the name.

And that wasn’t at all a hassle. Every month I had to contract someone to file a financial report for my entity, in person, at the local tax department. They also asked us to visit the local treasury office to pay for yearly license tax and everything, each visit consumed a few more hours. I had never felt more of my life coming past me at the time, feeling utterly useless and idle to some unbelivably great extent.

Above all, the (then) enterprise law dictated that you could only do businesses on categories that you were given written authorization to. That’s just another fcking bummer. The (updated) law tells you can do whatever the laws don’t see as illegal, which for sure opens up more freedom for the national business(wo)men to try out new waters and squeezed out some tiny chunks of corruption. Funny how people had to bribe to do business lawfully.

So it also came to be that I had to close my first and only business in the country. It hasn’t been a business per se, given we haven’t booked any active revenue. We didn’t do recruitments either, I was the sole legal representative cum CEO cum principal employee. That being said, on paper we looked like a ghost lingering for breaths and opportunities. That has been really the case here. I’m not going to be in the country for some time, so I think I need to close it down, to avoid complications. Once I heard they stopped a lady from traveling aboard because she neglected her tax duties. She forgot paying $100 or something, but that’s another story. I need to close this paper business down. And I’m glad to find the experience (after 3 years) has been way better.

First, I need to fulfill all the tax duties, that include submitting all the accounting reports on the entity’s income and VATs. That’s easy. What’s startling me is that they have pulled up an online system to let these reports be submitted online, securely and all. Heck, they (the tax department) even sends reminders on late submission. I was able to have direct communications with my tax officer, and the lady was giving prompt answers and above all, she was friendly.

Second, I need to pay some fees, and what startled me even more was I could do this online, from my bank account (using a different bank to the department’s bank), and the tax officer went out of her way to confirm those financial transactions. This saves me a bunch of travels, in Saigon’s heat. It’s just evolutionary, that’s what I’m thinking it is.

Third, admittedly closing down a business takes some talking back and forth, and going to places to return the stamp and everything, but you can always contract someone to do this for you, at an affordable price. I was referred to Gia Cat, and they charged me $75 for the complete package. That involves closing the tax account at the tax department, returning the stamp at the local police, and filing for closure at the department of planning and investment. What you’d be given in the end is a paper that tells you have creased your whole business, which means ultimate soul rest. I found Gia Cat a real lifesave, their staff even visited my place (which is 30 minutes away from their office on bike) to collect my signature for some missing papers.

All in all, I’ve ceased my business doings in Viet Nam but I’m hopeful about coming back.

CSV Processing (at a glance)

Not until recently did I find out Ruby’s native CSV.parse is not really performant. C’mon, loading 100_000 rows into memory? As if our beloved Ruby developer cum optimizer has not had enough on her table.

A quick Google search returned the smarter_csv gem. One more click led me to this writeup that planned out a better way when it comes to dealing with these nasty CSV processings.

I have only two things (which I deem relevant) to add to the above article:

  • If you’re a Sidekiq Pro user, Sidekiq offers Batches, whose handies include child jobs (say, you divide 100_000 rows into 100 jobs that take care of 10_000 rows each), callback (a nice email letting customers know his import was done will do) & statuses (how many child jobs are pending or have failed?)
  • If you’re concerned that each worker establishing a connection of its own will soon blow up your database instance, use pg_bouncer. Heroku users can use this handy buildpack the company has made available.
  • While having a direct route to your users table is nice and all, decently sized apps will come across cases when you have to do follow-ups when it comes to an import job. Things like: reindex ElasticSearch, spit out troublesome rows and let customers download these lines to perform revisions, generate log events, etc. My approach of preference is it’s best to steer away from doing these things. It should be the job of controllers or services, not the import workers. In an ideal environment, your worker(s) should only concern about splitting the file into chunks, put some nice headers to ‘em, and ping the relevant API(s) (ie: POST /api/v1/users) and let it do its job.

And yes, I ended up listing three things. I’m bad at numbers.

Thoughts on single-purpose services

1.

If you think about it, your simple CRUD Rails app is likely started out with a codebase and a service. People usually refer to MySQL/SQLite/PostgreSQL/etc as persistence layer, which in translation is a service that does a single purpose: persistence.

That is to say whether you like it or not, your app will always have to interact with external layers when it reaches certain complexity level.

2.

I used to determine whether a set of features fit in a problem domain, and thus extracted that set into a service. That worked for a while, until the cost of maintenance went too much for a small development team. Someone said we need decent monitoring infrastructure and deployment toolset before journeying into this land, and I can’t agree more. That being set, most of the times you need to sit down and rebuild your existing monolithic app on seperated classes that tie to problem domains, instead of spinning up a new fancy service.

3.

Ruby isn’t always the answer to everything. Not for persistence, for sure. And not for many others. Imagine you have to talk to multiple APIs to get the data you want, then process that data in order to make sense out of them. Parralel-ing these requests is possible with Ruby, also processing, but it will rake up your loading time to seconds. And we all know sad pandas will be sadder once they see seconds on the analytics dashboard.

Behind the scenes: Charity Map

It’s been three years since we jotted down the idea of what-is-now-called Charity Map.

Charity Map started out as a modest map of orphanages in Ho Chi Minh City. We felt compelled at the idea of visiting nearby orphanages, and was thrilled to find out our friends felt more involved if they knew of places within walking distance from their companies, so that they could do a quick visit over lunchtime, or after-work.

Three years have passed. My friends keep on asking me if Charity Map is still a thing, as if technology and charitable work in Viet Nam don’t go well aligned. So after some repetitive answers, this blog post landed.

Yes, it is still a thing.

What I gave away in this Quora answer still pretty much apply. But here are a few things not many people tell you, or they do but you haven’t realized how important they are yet:

  1. Doing charitable work in Viet Nam is hard. Corruptions, benefit groups and inefficiency are too common problems to be discussed.

  2. Charity groups aren’t too tech savvy. Pen and paper are dominant tools. I once found a big NPO (with donation influx of $50k+ daily) list all donations on a single web page, and their staff use that page to manage, verify, coordinate and do all kinds of things.

  3. There are unexplainable things. Be it well managed or having too many good volunteers, we survived three years with just a grant from Princeton University (and our balance sheet is still positive, weee). I reached out to the big NPO mentioned above offering free development work to build them a donation management platform, and was told off at once.

We also have tried a lot of things during the last three years. Charity Map’s flagship product is a crowdfunding platform that is parked at charity-map.org. We pulled off this project in early 2014 (after two months of full-time development work), and have let it run by itself ever since. As a technology organization that is dedicated to helping people do charitable work in Viet Nam, a lot of other ideas came into our picture and we tried (most of) them to the extent of human and financial resources we could afford.

One of the ideas is Charitio. Charitio lets businesses reward customers with charity credit, be it $0.1, $0.25, $1 or $10. Customers can accummulate their credit over a range of partner businesses, and use it to donate to charities of their choice. E-commerces in Viet Nam have long given away similar e-rewards, now it’s just a matter of letting the customers convert those rewards to charity credit on Charitio.

At the moment I’m writing this, it’s been a few months since a big ecommerce did a similar thing, except they sourced their own platform. They let customers convert their in-house credit to money so that customers could donate to a project the ecommerce prepicked. It all went well for a few weeks, until the said ecommerce found it costly to run this operation, customers found their choice of charitable projects limited, and the whole project went in limbo.

I’m not saying they should have partnered with us (well, I lied, they should have). But Charitio was made to do this. Our dev team has built a production-ready/tested API, we have worked with a lot of NPOs and understand well their needs and cases, and using our platform allows their customers to gather as much credit as possible on many other platforms that partner with us as well.

That being said, it’s an ongoing battle over the years. Three years are long but not as long. My work at the organization has always been plentiful, and I’ll always be around with my co-founder and fellow developers to fight this battle.

PS: I know I ended this post quite abruptly. Writing in 5*C has never been my thing, and my stay in Melbourne is timed, so I’m heading off to some warm place to enjoy the city as much as I can. See you soon with more posts when I get back to Viet Nam.

Notes on doing circuit breaker

This is Stoplight-specific.

.with_threshold excludes the first failure. Let’s say you want to retry the failed API call within 1 minute from the first failure, and ping the notifier (Log/Hipchat/Rollbar/etc) upon failing again:

begin
  Stoplight('example-1') { fail }
    .with_threshold(1)
    .with_timeout(60)
    .run
rescue Stoplight::Error::RedLight => e
  Rollbar.error(e)
end

Circuit breaking makes sense for consecutive failures (and retries off the hook); thus, you need to tackle the first occurrence of the failure properly.

begin
  Stoplight('example-1') { fail Api::Wrapper::ServerError }.run
rescue Api::Wrapper::ServerError => e
  'default_value'
end

Combine the two examples above, we have something like this:

begin
  Stoplight('example-1') { fail Api::Wrapper::ServerError }
    .with_threshold(1)
    .with_timeout(60)
    .run
rescue Api::Wrapper::ServerError, Stoplight::Error::RedLight => e
  Rollbar.error(e) if e.is_a? Stoplight::Error::RedLight
  'default_value'
end