普通视图

发现新文章,点击刷新页面。
昨天以前Fred Wu's Blog

Quick comparisons of Viture Pro and Xreal Air 2 Pro

2024年6月9日 18:08
I’ve had the Xreal Air 2 Pro for a while. I ordered the Viture Pro on the 23rd May, it shipped on the 28th May, and finally got delivered on the 9th June. I’m in Australia.

Packaging

The unboxing experience has been stellar, the packaging is one level above Xreal’s.

Case

I do like Xreal’s case not needing a zipper, but the utility of Viture’s case is better, with lining for both the glasses and the cable, whereas Xreal’s is simply a plain case without any inserts.

Build Quality

I would say on par, both are very well built.

Styling

This is highly subjective. I’ve seen many many reviews on both. For someone with a small head, I thought the Viture would look better on me… hmmm… not quite, I think I still prefer the Xreal. Having said that it’s not a big deal for me, I’d be mostly wearing them at home, and perhaps on flights.

Wearing Comfort

This one goes to the Xreal, by a large margin. Xreal’s leg ends are soft, bendable and very thin, making putting on and off the glasses a breeze. The Viture on the other hand, has hard leg ends, which requires more force to bend the whole legs outwards when putting on and off.

When the glasses are on my face, I can feel the plastic legs clamping on me with the Viture, whereas with the Xreal I almost don’t feel anything.

Screen Brightness

Xreal Air 2 Pro technically has lower nits (500 vs 1000 perceived), but in reality I couldn’t tell much of a difference. Viture Pro might appear ever so slightly brighter. Although, interestingly, the Viture Pro’s lowest brightness is lower than Xreal’s (although with significant contrast drop).

Also, Viture Pro has OSD to display brightness and audio levels when you adjust them, Xreal doesn’t.

Sound Quality

Disappointingly, to my ear, the Xreal Air 2 Pro sounds better, it is slightly louder, and has significantly better clarity.

Image Quality

This might be the most disappointing part, and I’m really hoping a new firmware will address this. As is, on the latest 0603 firmware, the Viture Pro has a significant green/yellow tint - like, not a little bit, but very very significant. This makes movie watching (my primary use case) a no-go.

Electrochromic Screens

Very similar between the two. Xreal has three levels, Viture Pro has two, but realistically only the 0% and 100% are useful anyway. One small thing I noticed is that when you push the button, Xreal changes the level immediately, Viture Pro has half a second delay, no biggy.

Screen Edge Clarity

I used to own a pair of Xrea Air (the 1st gen), and from memory, I think that one has the best screen edge clarity.

The Xreal Air 2 Pro notoriously has blurry edges, which I can confirm. After swapping to using the smallest nose pads, I was able to make the edges clear enough, but it still has a slight colour shift.

The Viture Pro is better, however the edges I would say still have a little bit of colour shift. Although, can’t say it effects my use case at all. I’ve tried to look at text, and they are fine.

Myopia Adjustments

This is one of the main reasons I bought the Viture Pro despite already owning the Xreal. As I’m a contact lens wearer, I’d like to use the glasses in bed without using contacts.

Obviously the Xreal doesn’t have this functionality. However, this is another major disappointment that I’m hoping a firmware could fix. When the myopia dials are set to 0, the screen is actually blurry!! I thought it was my eyes at first, but nope, I had to dial them both up slightly in order for the screen clarity to equal that of Xreal’s.

Verdict

As you can probably tell, I am very disappointed. As is, the Viture Pro is unusable with the significant green tint. And the usability is impacted by the miscalibrated myopia adjustments.

I’m hoping both of these issues will be fixed soon via firmware updates.

P.S. Tested both glasses on both macOS (Macbook Pro) and Samsung Dex (S24 Ultra).

Update a day later

So last night I used the Viture Pro in bed to test two things: comfort and myopia dials.

Myopia Adjustments

The myopia dials work. But as I suspected, it’s miscalibrated. As mentioned before, when I’m wearing contacts (new prescription, also tested with Xreal), I have to slightly dial them up to get a clear picture. Now when I’m in naked eyes without contacts, both of my eyes are -4.75, but I have to dial them all the way to the end (-5) to get the most clear picture. This indicates to me that the dials are miscalibrated.

In Bed Comfort

Comfort unfortunately is a step down compared to the Xreal. Mostly due to the rigid legs that stick out longer compared to the Xreal - they would push into the pillow. Of course, if you don’t plan on wearing them in bed this won’t effect you. Or if you have a larger head, it probably will be okay too.

Audio Control

Another thing I noticed is that Viture Pro’s volume control is separate to the system. When connected to the Mac, it is connected as a DisplayPort device type, whereas Xreal is connected as a USB device type. So when changing volume, Xreal directly changes the system volume whereas with Viture Pro, you can either change the volume on both the system and on the glasses or only on the glasses depending on the host device (on a Mac, you can’t change the system volume, on an Android you can). Personally I prefer the Xreal’s approach.

Colour Tint

Finally, I’ve taken some comparison shots of the colours between my Macbook Pro, Xreal Air 2 Pro and Viture Pro (this is also sent to Viture support).

Upon first glance they look very similar. But please zoom in and take a closer look. I’ve used the same camera and manually set the white balance to ensure they are consistent across the devices.

The colour shift is way more pronounced and is very noticeable in real life.

I think I’ve got a better comparison. The backgrounds are all close to black in real life, and pay no attention to the blurriness it’s just my camera’s focus. Again, set with the same white balance, you can now clearly see the yellowish tint on the Viture Pro. In real life the Macbook Pro and Xreal screens are very very similar.

]]>

Three Versions of The Three Body Problems, Thoughts from A Native Mandarin Speaker

2024年5月26日 15:51
Warning: Minor spoilers on the characters, but no spoilers on the story.

As a casual Sci-Fi enjoyer who mostly enjoys them through movies and TV shows, I’ve only occasionally read Sci-Fi novels. But I loved Andy Weir’s The Martian (loved the movie first, then went back to reading the novel) and Project Hail Mary. Therefore, since discovering Netflix’s 3 Body Problem not too long ago, I realised I had to start from the origin, and follow the thread from there, especially since I am a native Mandarin speaker.

The original novel, 三体, would translate to simply Three Body. However, the English translation of the book was published as The Three-Body Problem, and the Netflix adaptation 3 Body Problem.

Within the span of two months, I consumed, in order:

  • The original Chinese trilogy, which includes Three Body, Three Body II: The Dark Forest and Three Body III: Death’s End, in audio book format, totalling a whopping 85+ hours.
  • The Chinese TV series adaptation produced by Tencent Video, which consists of 30 episodes covering the first book, totalling 20 hours. You can watch the full series here on Youtube by one of the official publishers.
  • The Netflix TV series adaptation, which consists of 8 episodes covering the first book and parts of the second book, totalling 8 hours.

For the Chinese TV series, there is also a 26-episode Director’s Cut version, which apparently has better pacing by removing some original sub-plots and tightening the overall story flow. I actually didn’t mind the slower pace and the original sub-plots so I stuck with the original 30-episode version.

I’m going to rate all three versions, but as a benchmark, I’d give The Martian (both the novel and the movie) a 10/10, and Project Hail Mary also a 10/10. This is from the perspective of someone who grew up in China then spent more than 20 (and counting) years living in and breathing the Western culture.

TL;DR: Ratings Summary

| Version | Overall | Story | Main Characters or Cast | Minor Characters or Cast | Cinematography | Music / Soundtrack | VFX | Audio and SFX | — | — | — | — | — | — | — | — | — | Novel | 9 | 9 | 10 | 9 | N/A | N/A | N/A | N/A | Tencent | 9 | 9 | 10 | 6 | 10 | 9 | 7 | 5 | Netflix | 7 | 7 | 8 | 7 | 7 | 7 | 6 | 7

Story

| Version | Rating | Commentary | — | — | — | Novel | 9 | A very captivating story and with a world building that’s mostly grounded to reality. | Tencent | 9 | A condensed version that is faithful to the original novel (1st book), with some added minor characters and sub-plots. The story-telling focuses more on character building to push the story forward, just like in the novel. | Netflix | 7 | A “re-engineered” story with a much smaller, mishmashed ensemble, much faster pace and less convincing world building. It is the fast food version - not necessarily inferior, but much less nuanced and is produced for mass consumption by the Western audience. Remember, it speeds through the entire book 1 and part of book 2 in merely 8 hours, compared to the 20 hours of the Tencent adaptation for only book 1.

Main Characters or Cast

| Version | Rating | Commentary | — | — | — | Novel | 10 | The believable and multi-faceted characters is definitely a highlight of the series, especially from the lead characters of each book. | Tencent | 10 | Superb acting from the main cast. Shi Qiang is a highlight, the portray of this version of Shi Qiang adds more humour and lightens much of the heaviness of the story beats. Wang Miao has a calm mannerism and deep and soothing voice. Both the young and old Ye Wenjie are portrayed with consistency, and nuances that beyond words. | Netflix | 8 | The acting is decent for what the show is. Due to the fast pace nature, there is only so much that can be done by the otherwise fine cast. The characters themselves however, take a major hit. Many characters from the novel are combined. Worryingly, Ye Wenjie has been portrayed as simply bitter, missing the nuances entirely as a result of the many hours of character building in the novel and the Tencent adaptation.

Minor Characters or Cast

| Version | Rating | Commentary | — | — | — | Novel | 9 | Like the main characters, each minor character comes with a believable background and motivation, and helps with the story and world building. | Tencent | 6 | Unfortunately, this is a weak point of the Chinse adaptation. Whilst the characters themselves are fine, the cast unfortunately is a big hit and miss, with a few of them definitely miscast. Wei Cheng in particular, has been reduced to a one-dimensional maths nerd with a heavy Shandong accent unnecessarily. Pan Han’s portray is also wooden and uninspired. To top it off, a super minor character with only a minute or two of screen time has an extremely bizarre, over-the-top acting style that nearly ruined the entire scene. Luckily, most of the other cast ranges from okay (e.g. Shen Yufei) to superb (e.g. Chang Weisi). | Netflix | 7 | Acting from the minor cast is fine, nothing remarkable but nothing terrible either.

Cinematography

| Version | Rating | Commentary | — | — | — | Tencent | 10 | It’s a masterpiece of an art that left a huge mark on me. The cinematography is simply breathtaking. Even more, each episode includes a bunch of photo montages during credits. | Netflix | 7 | It’s mostly a standard sci-fi look and feel, it looks good but nothing too inspiring here.

Music / Soundtrack

| Version | Rating | Commentary | — | — | — | Tencent | 9 | A collection of finely composed soundtracks covering a wide range of emotions. I was moved by many of them. Check them out here. | Netflix | 7 | As a fan of Ramin Djawadi and his work from Game of Thrones and Westworld, this is a fine collection of soundtracks, albeit a tad forgettable compared to the aforementioned masterpieces.

VFX (Visual Effects)

| Version | Rating | Commentary | — | — | — | Tencent | 7 | Some VFX are a bit janky. Though in most cases the producers have hidden them well. The “in-game” CG and animation are very janky but that can simply be attributed to them being “in-game” therefore to be expected, besides, they are actually quite charming. | Netflix | 6 | This surprised me. I thought for sure Netflix would have the budget for higher quality VFX. In most cases this is true, unfortunately this is let down by some key moments having very questionable VFX and/or artistic choice.

Audio and SFX (Sound Effects)

| Version | Rating | Commentary | — | — | — | Tencent | 5 | Another unfortunate weakness of the Tencent adaptation. Many sound bites (such as the eery music) were simply lifted from other TV shows like Dark which takes me out of the immersion. What was also baffling, is the audio mixing - in more than one occasion, lines from different takes are stitched together with vastly different audio levels and uniformity. | Netflix | 7 | Like the cinematography, there is nothing wrong or special here. It’s good.

Extra: Languages

As someone who speaks both Mandarin and English, it’s been an interesting experience.

The Netflix version is easier to consume in this regard. Other than the character Mike Evan’s borderline gibberish Mandarin, for the most part I only needed subtitles for the few lines of French and Spanish that didn’t really impact the story. Although, Ye Zhetai speaking with a heavy southern Chinese accent is a bizarre casting choice.

The Tencent version on the other hand is much more difficult to consume. A few episodes in I had to rely on subtitles to make out both the Mandarin and the English spoken by the non-native speakers.

Final Thoughts

I liked the original novel trilogy and loved the cinematography of the Chinese TV adaptation. I probably would’ve liked the Netflix adaptation more if I had no point of reference from the other two.

If you’ve watched the Netflix version and are curious about the source material - read the novel(s) or watch the Chinese TV adaptation.

]]>

Tips for Job Interviews as a Junior Software Developer

2020年9月27日 13:56
As coding bootcamps such as Coder Academy and General Assembly churn out more and more software developers, and as more and more people start to realise the importance of software, companies these days are facing an increased amount of candidates applying for junior dev roles.

Recently we had to take down our job ad for a junior full stack React and Elixir role only a few days after posting it due to having received about 300 applications. Suffice to say, the competition is fierce at the entry-level end of the software engineer spectrum.

Software is eating the world. - Marc Andreessen

In the past three months alone I’ve had to sit through over a hundred interviews, many of which were for junior roles. Some candidates did really well but unfortunately the vast majority did quite poorly. In this article I am hoping to offer some tips and insights as a seasoned interviewer and hiring manager, to those who might be struggling landing an entry-level software engineer role.

I’ll break the tips into three sections: before an interview, during an interview and after an interview.

Before an Interview

This is arguably the most important stage of your interview - before it happens! Prepare yourself with enough knowledge and confidence in both software development and conversational skills in order to ace the interview and stand out amongst a sea of candidates.

Study Software Engineering Fundamentals

I get it, bootcamps and many online tutorials and courses focus on teaching the practical things to turn code into products. But just like running or even walking without correct postures, it can be incredibly dangerous over time.

My recommendation is to read on the basics of object-oriented programming, functional programming and some design patterns. For instance, build up some basic understanding of how the JavaScript prototype works or how the Ruby object model works can be extremely beneficial in progressing your technical capability on application design and architecture.

Get More Project Experience

Not having any substantial project experience on your CV is a sure way to significantly lower your chance of getting an interview.

Do junior developers really need substantial project experience, you may ask? Abso-fucking-lutely!

Build your own side projects, participate in open source projects, pick up some freelancing work - the choice is yours. These won’t be your most technically accomplished work, but they will help you accumulate experience, and more importantly to demonstrate your capability to your interviewers with real world experience.

Why Software Development?

I want you to put your hand on heart and answer this question honestly. If your answer is more money or FOMO, that’s okay, but be prepared to be disappointed with the reality - if you are not in this field because of your passion for building products or solving problems, you will find this a long and hard slog.

If you are passionate about building software, then let me tell you this - when you are starting your career you will have to give up your work life balance for a bit. That is, if you want to boost your career and progress quickly beyond being a junior developer.

My interest is in the future, because I am going to spend the rest of my life there. - Charles Kettering

Looking back at when I started my software career many moons ago, I never regretted giving up my social life early on in my career: I worked extremely hard, often did many jobs - a day job as a dev, a night job as a dev, freelancing and doing open source work all at the same time. Some would probably frown and say that I had no life, but I choose to believe that I was making sacrifices so I can become good at what I love doing in the shortest amount of time.

Research the Company You Apply For

If you are one of those who spam your CVs to every job opening, please consider changing your tactic. A well written cover letter will not only increase your chance of getting an interview, but also allow you to ask relevant questions during the interview.

Check out the company’s website and any social media pages on LinkedIn, Facebook and Instagram, etc. Do a bit of “stalking” on the people who work in the company, sometimes this will give you some insights into the type of work environment and culture the company has.

Doing enough research is not only for your own good - knowing whether you will enjoy working there should you end up getting a job offer, but also demonstrates your ability to do research and independent thinking to your interviewers.

Read, Listen and Communicate

This one might not be immediately obvious because it’s very high level and somewhat vague, bear with me.

Some people might be deep thinkers but really struggle to get their points across, and to communicate their ideas and thought process in a clear, concise and useful way. My advice is to read blogs, listen to talks and learn from other people on how they communicate. When you read and listen, pay more attention to how sentences are structured and how titbits of information are given.

The most powerful person in the world is the story teller. - Steve Jobs

And don’t forget to practice! Get comfortable talking to others on various topics in software development.

During an Interview

Hopefully the tips given thus far would help you secure an interview. Now it’s show time!

Turn Up On time, Have Equipment Sorted

Whether you are attending an on-location interview, or more likely during Covid, an online interview, make sure you turn up on time!

If it’s an online interview, also make sure you have whatever software needed (Zoom, etc) installed and tested. I’ve had a few occasions where the first five minutes of the interview was spent on the candidate trying to get their mic or speaker working - it’s not a huge deal, but it does show the lack of preparation and organisational skills.

A Smile Goes a Long Way

Understandably, interviewing is stressful. However, remember to put on a smile when you greet your interviewer and when you do small talks. Everything needs a balance of course, when you’re giving technical answers it’s probably not a good idea to smile.

If you know you get uncomfortable talking to strangers or when under pressure - do more practices! If you can’t find a friend or don’t have a partner to practice with, use a mirror!

Learn to Say “No, I don’t know”

When you don’t know something or can’t recall the details of something, just say it. As an interviewer, when I ask you a technical question I can see through your hmms and ahs. A “sorry, I don’t know” is a much better answer than one that doesn’t make sense.

Bonus point: “Sorry I don’t know, but I will look it up after this interview.”

Read Social Cues

This one can be extremely easy or extremely hard depending on your personality. For those who might struggle with reading social cues, try pay more attention to what type of questions you were asked.

If it was an introduction of yourself to kick off the interview, make it more than just two sentences but also under five minutes - this is usually a warm up for you to find your feet and calm your nerve, and for the interviewer to extract any interesting information as a discussion point later. It doesn’t have to be your memoir and you shouldn’t robotically talk through your entire education and work history.

If it was a technical question, always try to time-box it. Don’t drag on and repeat yourself just because you have already answered it quickly and concisely. When in doubt, ask the interviewers whether they have anything in particular they were looking for.

If it was an open ended question, try to explain your thought process as you explain it. As an interviewer, I’d hate to ask an open ended question and get a short yes/no back. At the same time, give interviewers room to interject and ask follow-up questions by pacing yourself.

Don’t Speak Too Fast

Okay, you might be very nervous, or English might not be your first language, so you tend to speak fast to “get through it”.

If that’s the case, work on your speech pattern, make it concious enough that you will recognise it when you speak too fast. If interviewers can’t get your points clearly, they might not always ask you to clarify - because if you kept doing it repeatedly, you would already be written off as a suitable candidate.

Be Humble

I totally understand that in a competitive job market you want to present yourself in the best light. Just be confident and speak the truths.

As someone who’s been doing interviews for years, I really dislike people who overstate their capability either on their CV or during the interview because these are often very obvious. Claiming to be an Elixir expert but not knowing how the supervision tree works is a sure way to score a black mark, for example.

Show Passion and Drive

To counter the point on over-selling yourself, what works better is to show interviewers your willingness and determination to learn. Remember, you are still a junior therefore we don’t expect you to know everything we know.

What we do expect, is your commitment to learning - not by saying it, but by demonstrating it. Remember the earlier tip on getting more project experience?

Ask Questions

Usually towards the end of the interview, we’d ask you if you have any questions. Don’t say “no”.

Doesn’t matter how much research you’ve done on a company, there are always insights someone who’s working at the company can give you. How do they manage the delivery? How do they support learning? What makes them enjoy working at the company?

Use this opportunity to gain insights into why you should be working at this company, and to demonstrate your interests in working at the company to the interviewers too.

After an Interview

After completing an interview, it’s an opportunity to do a little retrospective and do a follow up when necessary.

Close the Knowledge Gap

Don’t over-analyse on what you could have or should have said. Instead, spend your energy on solidifying the topics you couldn’t or didn’t provide good answers for. If you’ve said “don’t know” or “not sure” during the interview, now is your chance to make it “sure”.

Follow Up

If during the interview there have been things mentioned that you could provide links or more information on, be sure to act on it. You never know, maybe the clarification is just what the interviewers needed to help them make a decision.

~

To all the junior developers out there - enjoy the journey! You are in for an amazing ride, make every second count.


If you enjoyed this article, checkout my other tips articles:

]]>

Tips for Becoming a Better Software Developer

2020年5月3日 14:34
Over the past decade or two, both as a software developer and as a manager I have accumulated a few tips for becoming a better software developer.

For context, I’ve been working as a software developer for more than a decade, mostly in small product-based companies; over the last decade or so I’ve worked as a team lead or manager in various capacities; and I’ve moonlighted as a consultant, freelancer and open source contributor in-between. Check out my LinkedIn profile and Github profile if you are interested.

Now, let’s break down the tips into three main areas: mindset, technical and people. The goal of this article is to raise awareness of the topics mentioned, if you want to understand more on a particular topic I encourage you to do your own research.

Mindset

Have a Growth Mindset

This one hopefully should be obvious, if you haven’t heard of it, please read more here.

Have a StarCraft Mindset

This one is “new”, I’ve given a talk that includes more details on what it means, check it out here. In essence, it’s a mindset that requires you to always consider things around you, and forces you to think beyond your narrowly focused task at hand.

Recognise the Dunning–Kruger Effect

I once interviewed a mid-level developer who rated himself as a 9.9 out of 10. The Dunning-Kruger effect is essentially the opposite of the Imposter Syndrome, both are very common in our field.

Agile is Not a Process, It’s a Mindset

Some people are overly obsessed with the processes of “being agile”. In truth, every organisation, every project and every team is different, there is no point to follow a set of processes that might slow you down and not add any value. Discover what being agile means to your team, your project and your organisation is an ongoing effort and should be treated as such.

Different Organisations Require Different Engineering Approaches

Some projects and organisations require tech-driven decisions be made upfront, some on the other hand require product-driven decisions instead. Knowing how your organisation operates and what it values plays a key part in understanding how software development should be approached.

A Sense of Entitlement Only Gets You So Far

Let’s be honest, as a software developer we are extremely lucky to be in a high demand field. As a result, many have started building a sense of entitlement - some ex-colleagues of mine even complained about the lack of tissue boxes in the office. Starting and running a business is extremely hard work, let’s all appreciate what we have before throwing a tantrum.

9-5 is Not Evil, Nor is Crazy Work Hours

Every now and then I see conflicts between people who share different philosophies on what work and career mean to them. We should embrace the different ways people prefer to spend their time and energy on, rather than force our own ideology on them. Discovering and knowing the difference between working at a giant corporation versus working at a small start-up is key to our happiness.

Pain and Gain, There’s Always a Trade-off

An extension of the 9-5 vs crazy work hours: adjust your expectation based on the effort you put in. You can’t have everything, prioritising family life over career building is 100% okay, as long as you know what you are giving up on, vice versa. In my 20s, many of my similarly aged friends and colleagues would go out and have fun after work and on weekends while I was in front of my computer building open source projects, is an example of a conscious trade-off I made.

Speed vs Quality

Internalising the need for speed and the need for quality should always be on our mind. Pursuing only quality or only speed will significantly limit your problem-solving capability as well as your career progression.

Think, Then Do

It’s a balance - don’t overthink it, but also don’t do without think first. When in doubt, ask more experienced people for advice and guidance.

Technical

Avoid Second System Syndrome

I’ve seen Second System Syndrome over and over again throughout my career. Some people always assume a rewrite is a much better approach than alternatives, more often than not though, it is not the case.

Micro-Services is Neither New nor the Holy Grail

There’s the age-long debate of monolith vs micro-services architecture, and SOA (Service-Oriented Architecture) has been around for decades. Each has its own pros and cons, don’t get bought into the hype because a blog post says so, or a particular product and company found it successful. Use the right tool for the right job.

TDD (Test-Driven Development) is not the Holy Grail

Just like micro-services, TDD is not the holy grail and should not be treated as much. Chasing the TDD dream without knowing its pros and cons is just as counter-productive as not doing TDD when necessary.

Code Aesthetics Matter

A lot of people treat coding as more of a scientific endeavour: they focus purely on the algorithms and results. Software development to me is a creative endeavour, and I often use white spaces and the general look and feel of the code base to determine how well-organised a project is - deeply nested code and long functions for instance are obvious ways to determine potential code smell.

You’ve all heard of the SOLID principles, but have you heard of the CRAP principles?

Read As Much Code As Possible

Don’t get me wrong, writing code is important, practice is important, but time and time again I come across developers who clearly have little exposure to established patterns and conventions.

Tech Stack Exposure and Diversity are Always a Good Thing

No matter whether you are in a highly specialised field or being a generalist, having exposure to different tech stacks and paradigms is always a good thing - it widens your field of vision and increases the boundary of your technical understanding. Don’t be the guy or gal who is known as “a \ developer”.

Have a Deeper Technical Understanding Helps

This is strictly speaking a “nice to have”, but I lost count of the number of candidates I interviewed who claimed to be a Ruby expert yet cannot explain the Ruby object model and have never heard of eigenclasses, or senior developers who cannot explain the difference between git merge and git rebase. Sure, you may not need the deep understanding in order to do your job, but it will certainly help!

People

Always Assume Incompetence over Malice

If you haven’t, read more about it here. Knowing this, and paired with having a growth mindset, you should therefore be encouraged to teach, educate and influence, rather than being defensive and participate in “us vs them”.

Truth = Visibility x Tolerance

This is another topic covered in my talk. If you find yourself regularly dissatisfied with the information you receive, ask yourself: what information is and isn’t available to you, and are you sure you have the stomach to tolerate the new information?

Rapport and Productivity are Built Over Time

Some people assume a team’s productivity is determined largely by team member’s individual capability, some also assume a 10x engineer is a reflection on one’s technical capability. It’s not. Productivity evolves over time - building rapport with those around you is often the unsung hero in productivity.

Champion Team Over Champion Individual

This one hopefully is obvious, but I just have to emphasis its importance. Please, if you struggle to work with your team, ask for help! If you have a team mate who is difficult to work with, reach out and help them!

Final Thoughts

Software development is evolving rapidly, some fundamentals however are always useful and relevant. I hope you find this article useful.

I’ve mentioned my talk a few times, but if you haven’t already, check it out below, it covers a few topics mentioned in this article, and some more. Also check out my tiny Youtube channel for more tech talks I’ve done in the past.

video: https://www.youtube.com/watch?v=MBczdO7RgNo


If you enjoyed this article, checkout my other tips articles:

]]>

Launching Focussist Landing Page - An Upcoming Agile Project Management Tool

2020年3月23日 19:04
Over the past few years as I gain more and more experience in not only building software and products but also in leading teams and projects, I realised that my desire of wanting an agile tool that’s super simple to use yet super flexible to adapt to different needs has grown stronger and stronger.

Given the COVID-19 situation that’s going on at the moment, I’ve finally decided to spend a few nights working on a pitch deck for myself to validate the value proposition, and some high level wireframes to visualise the ideas.

Over the weekend I’ve put together a landing page to “market” my ideas. It’s a quick job, but the underlying thinking has been on the back of my mind for years now.

Introducing Focussist, or rather Focussist’s landing page: Focussist.com.

Please check it out and give me your feedback, and don’t forget to sign up. ;)

]]>

Conference Talk: Adaptable Human @ RubyConf China 2019

2019年9月14日 16:05
Seven years ago in 2012 I spoke at RubyConf China 2012. It was a technical talk on how to become a better developer, if you’re interested you can check out the video recording.

Seven years later in August 2019, I headed back to Shanghai again to speak at this year’s RubyConf China. And this time around, it was a non-technical talk on how to develop one’s career. Check out the video recording below:

video: https://www.youtube.com/watch?v=MBczdO7RgNo

And, here are some photos of yours truly. :)

]]>

New Blog with New Design and Gatsby.js - Loving JavaScript Again

2019年1月20日 19:27
In 2010 I set up this blog on Tumblr mostly due to the ease of publishing and not having to worry about the hosting. I also went through two design iterations done in Photoshop:

You can probably tell that these designs were done before the flat and minimalist design trend we are seeing in recent years.

As I decided to revamp my blog once again, I thought I’d use Sketch this time around and aim for a simpler, cleaner and more mature design approach that’s quicker to design and to build. And this is exactly what I did:

Thanks to Sketch, the design process was much better than I would’ve had in Photoshop.

Why the Revamp?

A big part of the reason for the blog revamp was to move away from Tumblr, now that it is part of the dying Yahoo!, and to invest in modern technologies to make the blog function better by adding responsive design and a proper grid system, etc.

As I was reading up on and researching technologies as I often do, I came across Gatsby, and I was immediately convinced that this was going to power my new blog as it appears super fast (by building static content and pre-fetching), is based on React and has a huge collection of useful plugins.

At work I often joke about how I hate JavaScript (rm -rf node_modules anyone?), the truth is I started off my career doing a lot of JavaScript, my most popular open source library by GitHub star count is in JavaScript, and I have always worked as a full stack dev until recent years.

Due to the shift of my responsibilities more into leadership over the past few years, I have opted to focus more on the backend, but deep down I love software in general and given the frontend scene has seen some major improvements in the past few years, not to mention that at work we have several React code bases, it’s time I dig my teeth into it again.

Disclaimer: until recently I had only done two React projects before and my exposure to ES6 has been somewhat limited.

Gatsby

The thing about Gatsby that impressed me the most is how fast it is due to its out-of-box support for pre-fetching. With a wealth of different starter templates it is also very easy to hit the ground running. I used gatsby-starter-netlify-cms as it supports Netlify and is fairly simple.

Export Tumblr Posts

First thing first, I will need to export all my tumblr posts into Markdown files so that Gatsby can consume them. There are heaps of such tools including Tumblr’s own export functionality. I ended up having to use these two:

  • Jekyll importer for converting all Tumblr posts into Markdown
  • tumblr-utils for downloading all images in the posts (Tumblr’s own export tool and many others only download images hosted on Tumblr)

Of course, many search-and-replace as well as manual massaging were required to get the new Markdown files to a useable state.

Add More Gatsby Plugins

The starter template was great, but I needed more functionalities so I ended up adding these over time:

Using many of these plugins only requires you to add the plugin itself and some config, then off you go - it could not be any easier!

GraphQL

One other reason why I was interested in Gatsby was because of its out-of-box support for GraphQL. I came across GraphQL a while back but never had the chance to work with it. At work one of our projects started using GraphQL and the team behind it rates it highly, so I really wanted to get some first hand experience using it.

With the inclusion of GraphiQL the in-browser GraphQL IDE, the GraphQL experience in Gatsby is extremely simple and satisfying.

Querying Data using Gatsby’s Page Components and StaticQuery

It’s been a bit of journey.

The starter template I used has GraphQL queries in the page components, but I wanted the GraphQL queries in shareable components so I opted to using Gatsby’s StaticQuery.

Until I decided to add gatsby-awesome-pagination which requires the GraphQL queries to have variables therefore incompatible with StaticQuery.

Despite the back and forth between page components and StaticQuery, I still loved the GraphQL experience in Gatsby.

It’s All Just React (In a Good Way)

The great thing about Gatsby is that despite making things easier by including a bunch of useful functionalities and plugins, down to its core, everything is based on React Components.

What this means is that as long as you have a working knowledge of React and React Components, extending Gatsby’s capability is simple and straightforward.

CSS Powered by Bulma

The starter template also includes Bulma - a modern and popular CSS framework.

Until now I’ve always relied mostly on Bootstrap so it’s nice to get to experience another CSS framework. And it turns out, Bulma is quite nice and easy to use too.

Thanks to Bulma, supporting responsive design was a breeze.

Gotcha: CSS Rules and “gatsby-plugin-purgecss”

gatsby-plugin-purgecss is included as part of the starter template to reduce the size of the CSS files by removing unused CSS rules. It’s wonderful as it saves about 200Kb of CSS (before minifying and gzipping) from unused rules from Bulma.

One gotcha I soon realised though, is that it also purges any CSS rules that are only rendered in the Markdown components. My guess is that the purging happens before the static contents were rendered therefore it cannot detect them.

The fix was quite simple, given that by default it only purges the main css file (in my case, all.scss), and all the specific rules in this file were defined by me, all I had to do was to add the comment lines to tell the plugin not to purge these rules:

@import "~bulma";

/* purgecss start ignore */
.my-own-css-rules-here {
}
/* purgecss end ignore */

Gotcha: CSS Conflicts Between Bulma and Prism.js

One more issue with CSS was the conflicts between Bulma and Prism.js due to the way some of the CSS rules are defined in Bulma.

What I had to do was to “reset” those CSS rules so they can inherit from Prism.js instead:

.content {
  .number,
  .tag {
    align-items: inherit;
    background-color: inherit;
    border-radius: inherit;
    color: inherit;
    display: inherit;
    font-size: inherit;
    height: inherit;
    justify-content: inherit;
    line-height: inherit;
    margin-right: inherit;
    min-width: inherit;
    padding: inherit;
    text-align: inherit;
    vertical-align: inherit;
    white-space: inherit;
  }
}

Netlify: Deployment and Hosting Made Easy (and Free!)

Before Netlify and similar services were available, many people used GitHub Pages to host their static content sites. It worked reasonably well but in the end this isn’t GitHub’s core competency and the features are limited.

Netlify takes deployment of static content sites to a whole new level and I cannot be happier to have finally jumped on the bandwagon too.

Closing

Suffice to say, I am very satisfied with this revamp experience - Sketch, Gatsby, GraphQL, Netlify and many other software have made it a walk in the park.

It only took me around 50 hours to get to 99%, and a few hours more to add in things like Disqus and social sharing.

Oh, and for those who are curious, the source code of this blog is available on GitHub here.

Let me just say:

The last time I fell in love with JavaScript was when JQuery first came to the scene many moons ago. Ever since then I dread working on the JS stack mostly because I’ve been spoiled by the Ruby and Elixir ecosystems. It’s nice to fall in love JavaScript again after so many years of simply “getting by”.

I hope you like my new blog, and if you have a similar experience to share, I’d love to hear from you!

]]>

Elixir and Doctest - Help Writing Better Programs, One Function At A Time

2017年8月7日 18:00
Preface

If memory serves right, it’s been several years since I first dabbled in Elixir, but it was about a year ago I really started putting some serious effort into learning Elixir, and as a result I made two libraries in the machine learning space: Simple Bayes, a Naive Bayes text classifier implementation, and Stemmer, an English (Porter2) stemming implementation.

Unfortunately after I’ve released those two libraries, I hadn’t had much opportunities to work with Elixir. My day jobs have been mostly Ruby, JavaScript, PHP and a dash of Golang. And so, after being silent for a year, I’ve decided to pick up something I had started a year ago - a web Crawler. If you are new to Elixir, feel free to follow this project as I am actively developing it.

Learnings

The preface is to give a bit of background of when and how I started learning Elixir, now, let me talk about one of my favourite features of Elixir, and how it helps me write better code not just in Elixir, but in virtually any other language.

Introducing the topic of today, a really simple feature, and in fact it has been part of Python for years - the doctest.

Doctest

In short, a doctest is pieces of code examples that run as part of the test suite, and show up as part of the documentation. For example:

defmodule Greeting do
  @doc """
  ## Examples

      iex> hello("world")
      "hello world"

      iex> hello("dear")
      "hello dear"
  """
  def hello(input) do
    "hello #{input}"
  end
end

And then all you need is in your corresponding test file, to enable doctest:

defmodule GreetingTest do
  use ExUnit.Case

  doctest Greeting
end

Even though I knew about Python’s doctest for years, I’d never realised how impactful it can be given its simplistic nature, probably due to the fact that I’ve never used Python for anything serious.

Until Elixir.

There are three things I find the most impactful as I write more doctests: clarity, scope, and design.

Clarity

As a ruby programmer, I appreciate greatly the beauty of not just the main code base, but also its test suite. Hence I’ve always preferred to use RSpec in larger code bases. However, as the application gets more complex, and as the number of test files grows, the cognitive overhead of reading and processing all the files and lines becomes higher and higher.

Doctest solves this perfectly - no longer do we have to crawl through the right file and the right line for a particular test case, all the test cases are neatly presented right in front of you as you read the function itself.

You might think this as trivial, but just like many organisations spend time and effort to optimise for effective communication by studying proxemics, proxemics between different components of a software code base also plays a role in improving the code clarity, and ultimately the code quality.

Scope

Doctest is purposely simple, and is designed for unit tests. There have been many times when I found myself realising my function was too dependant on external states, or are doing too many things because it was hard to write simple doctests. In a way, the constraints of doctests have forced me to rethink the scope of my function, and that would often lead to an overall better designed system.

Design

As much as I’d like to think about the SOLID principles all the time, it is often too easy to dig deep wholes in the midst of building things.

Every now and then I find myself extracting a piece of logic to a private function and calling it a day. In Elixir, only public functions can have doctests - again, this constraint pushes you to think about the importance and the role of a particular function, perhaps it is better to be moved to another module as a public function therefore can have its own doctests. Here is an example when I did some refactorings on Crawler.

Closing

I wanted to write this article for a while now - as I truly love and appreciate Elixir’s asthetics and features. Many developers might find functional programming as a barrier, but I can assure you that with Elixir’s tooling and ecosystem, and of course doctest (wink), building software feels like a breeze.

Last time when I was this happy building software was when I first discovered ruby. If you haven’t given Elixir a try yet, I encourage you to do so sooner rather than later, it will not only give you a functional programming perspective, but will also help you write better code in other languages.

]]>

I Accidentally Some Machine Learning - My Story of A Month of Learning Elixir

2016年7月24日 02:33
About a month ago I was in-between jobs - I had two weeks to rest up, recharge and get ready for my new job. So I thought, I should use those two weeks to learn something new.

Years ago I briefly looked into Elixir when it was first released to the wild, at the time I wasn’t interested in picking it up due to its syntax similarity to Ruby, despite their vastly different underlying semantics. I love Ruby, and it’s been my weapon of choice for the past 6-7 years, so when it came time for me to learn something new, I naturally wanted to learn something a bit more different than Ruby, syntax-wise.

Fast-forward a few years, I am more mature and open-minded, and are now in a position to welcome Elixir and to embrace the Ruby-like syntax as well as the functional programming mindset with open arms.

Learning Elixir

Given the strong influence of Ruby in Elixir, I can’t help but get nostalgic about learning it by reading another great book by Dave Thomas: Programming Elixir.

Dave Thomas’ original Programming Ruby was instrumental in the success of the Ruby programming language and its communities in the west, and it certainly has helped me to greatly widen my exposure to not only the wonderful world of Ruby but object-oriented programming in general as a PHP developer.

Learning Elixir has been really fun, not only does it share the same developer-friendliness championed by Ruby, it also does so with remarkable strength in concurrency thanks to BEAM (Erlang’s VM).

Many people are drawn to Elixir due to its Ruby influence, its functional and immutability nature, and its Actor-based concurrency model. I would however like to call out one of my favourite features of Elixir that sometimes gets overlooked, and that is how you could write unit tests using ExUnit.DocTest.

Elixir Makes Writing Unit Tests Effortless

Python programmers have been writing doctests for years, but Ruby due to not having an equivalent standard library, has never had writing doctests taken off in its community. I’m glad Elixir has.

Take a look at the code snippet below:

defmodule Stemmer.Step0 do
  @doc """
  ## Examples

      iex> Stemmer.Step0.trim_apostrophes("'ok")
      "ok"

      iex> Stemmer.Step0.trim_apostrophes("o'k")
      "o'k"

      iex> Stemmer.Step0.trim_apostrophes("'o'k'")
      "o'k"
  """
  def trim_apostrophes(word) do
    word
    |> String.replace_prefix("'", "")
    |> String.replace_suffix("'", "")
  end
end

The three test cases given will actually be tested by ExUnit, provided you ask it to do so as below:

defmodule Stemmer.Step0Test do
  use ExUnit.Case, async: true

  doctest Stemmer.Step0
end

You may think having doctests is not a big deal, what I can say is that I am really enjoying the fact that unit tests can be written with minimal friction and high visibility (as they sit with the implementation, rather than within a big test suite with a sea of files). Besides, not all of us practice TDD religiously or 100% of the time, and when I don’t or can’t do TDD, having doctests ensures I don’t forget adding test cases. :)

Toy Robot in Elixir

The best way to learn something new is to practice it as you learn it. Instead of diving straight into a complicated system, or to write a hello world program, I thought I would dig out a code test and re-implement it in Elixir.

The code test is the rather infamous Toy Robot test. As an interviewer, I must have reviewed a few hundreds of these tests, most of which in Ruby. I know what a good OO solution looks like, so naturally I was looking forward to “rethink” the problem and have a crack at it using Elixir.

Here is the result: https://github.com/fredwu/toy-robot-elixir

As a learner, I wanted to practice as many language features as possible, so included in the Toy Robot test code I tried:

  • Different data structures such as Struct and List
  • Pattern matching
  • Data piping (|>)
  • Agent for managing states
  • Macros for validation rules

I kept the test code small and nimble on purpose to illustrate the readability of a highly expressive language. I may not have exercised all the language features (such as supervisors and protocols), but at that point I felt I could start the Elixir journey with a big smile on my face already.

Learning Phoenix

I have to admit, a big part of the reason that got me started on learning Elixir is to build a side project. Initially I wanted to simply use Ruby and Rails because I can be very productive using them, but in the end I decided on learning Elixir and Phoenix, because most side projects fail, and when they do, how much learning can you extract out of those experiences? My answer to this question, is to learn a new language, a new framework and most importantly a new programming paradigm to drastically increase the amount of learning I could gain.

But please, before you jump on Phoenix, learn Elixir properly first! Over the years I’ve seen far too many cases of people jumping on Rails without understanding the language features of Ruby…

To learn Phoenix, the Programming Phoenix book is pretty much the bible on this subject aside from the official guide, and it is written by Chris McCord, the author of Phoenix, Bruce Tate, and Jose Valim, the author of Elixir.

As an experienced Rails developer, it only took me a day or two to go through the book (I largely skimmed the chapters on Channels as I don’t intend to build a real-time app).

Now, as a Ruby developer, the biggest pain point when working on Rails or other sizeable Ruby MVC projects for me, is ActiveRecord. I got so frustrated to the status quo I even attempted to fix it.

Fortunately, in Elixir and Phoenix we have Ecto. One of my favourite features of Ecto is the concept of Changeset.

Take a look at the code snippet below:

defmodule MySecretApp.User do
  use MySecretApp.Web, :model

  schema "users" do
    field :username, :string
    field :email, :string
    field :password, :string, virtual: true
    field :encrypted_password, :string

    has_one :profile, MySecretApp.Profile

    timestamps()
  end

  def changeset(struct, params \\ %{}) do
    struct
    |> cast(params, [:username, :email])
    |> validate_required([:username, :email])
    |> validate_length(:username, min: 3, max: 20)
    |> validate_format(:username, ~r/\A[a-zA-Z0-9_]+\z/, message: "alphanumeric and underscores only")
    |> validate_format(:email, ~r/@/)
    |> unique_constraint(:username)
    |> unique_constraint(:email)
  end

  def creation_changeset(struct, params \\ %{}) do
    struct
    |> changeset(params)
    |> password_changeset(params)
  end


  defp password_changeset(struct, params) do
    struct
    |> cast(params, [:password])
    |> validate_required([:password])
    |> validate_length(:password, min: 8, max: 200)
    |> encrypt_password
  end

  defp encrypt_password(changeset) do
    case changeset do
      %Ecto.Changeset{valid?: true, changes: %{password: password}} ->
        put_change(changeset, :encrypted_password, Comeonin.Bcrypt.hashpwsalt(password))
      _ ->
        changeset
    end
  end
end

The changset/2 function can be used when a user needs to be updated, whereas the creation_changeset/2 is to be used only when a user is first created. Sure, you can achieve similar result in Rails by using custom validators, but the fact that this practice is enforced by the library and the framework, is encouraging.

One other thing that I’ve seen in larger Rails apps, is the leaky abstraction of view-level logic, they typically sit in controllers, helpers (which are globally available) or worse, models. Phoenix in this case follows what Hanami, Trailblazer and many other frameworks do: it introduces a “view model” layer.

Something along the lines of:

defmodule MySecretApp.UserView do
  use MySecretApp.Web, :view

  def full_name do
    "#{title} #{first_name} #{last_name}"
  end
end

In a nutshell, Phoenix is just like Rails, but less magical (in a good way) and faster. :) Like the Elixir eco-system, most libraries and concepts feel like their ruby/rails counterparts, but more refined.

There are a few popular Hex packages if you’re familiar with their ruby counterparts:

Elixir Ruby
Code analysis credo rubocop
Testing espec rspec
Browser testing wallaby capybara
Test coverage excoveralls simplecov
Test factory ex_machina factory_girl
Authentication guardian devise

And the list goes on and on… Be sure to check out Awesome Elixir for more community curated Elixir libraries.

Learning Machine Learning

As I started building the foundation of my side project, you know, the usual user management and session management, etc, etc, by chance I came across mentions of Bayesian inference.

One thing led to another, very soon I started looking into varies different machine learning algorithms such as Naive Bayes and Random Forest. These are all subjects I had never come across or even heard of before, as I have no strong mathematics, statistics or computer science background. I was however, intrigued and inspired nonetheless, and wanted to employ some machine learning in my side project.

And the best way to understand and learn about machine learning algorithms? You guessed, is to write one! After some research, I came to the conclusion that Naive Bayes is one of the simplest to implement, is great for text classification which is useful for my side project, and has good accuracy given its simplicity and fast speed.

Introducing Simple Bayes

So, after a few train rides to and from work, on my newly purchased Macbook (yes, the one with only a single USB-C port), I built a Naive Bayes library: Simple Bayes.

As of writing, this is the only Naive Bayes library in Elixir that supports the following features:

  • Naive Bayes algorithm with different models
    • Multinomial
    • Binarized (boolean) multinomial
    • Bernoulli
  • No external dependencies
  • Ignores stop words
  • Additive smoothing
  • TF-IDF
  • Optional keywords weighting
  • Optional word stemming via Stemmer

Let’s see it in action shall we? :)

SimpleBayes.init
|> SimpleBayes.train(:ruby, "I enjoyed using Rails and ActiveRecord for the most part.")
|> SimpleBayes.train(:ruby, "The Ruby community is awesome.")
|> SimpleBayes.train(:ruby, "There is a new framework called Hanami that's promising.")
|> SimpleBayes.train(:ruby, "Please learn Ruby before you learn Rails.")
|> SimpleBayes.train(:ruby, "We use Rails at work.")
|> SimpleBayes.train(:elixir, "It has Phoenix which is a Rails-like framework.")
|> SimpleBayes.train(:elixir, "Its author is a Rails core member, Jose Valim.")
|> SimpleBayes.train(:elixir, "Phoenix and Rails are on many levels, comparable.")
|> SimpleBayes.train(:elixir, "Phoenix has great performance.")
|> SimpleBayes.train(:elixir, "I love Elixir.")
|> SimpleBayes.train(:php, "I haven't written any PHP in years.")
|> SimpleBayes.train(:php, "The PHP framework Laravel is inspired by Rails.")
|> SimpleBayes.classify("I wrote some Rails code at work today.")
# => [
# ruby: 0.20761437345986136,
# elixir: 0.08101868169313056,
# php: 0.019047884912605735
# ]

As you can see, provided with reasonable training data, Naive Bayes can work extremely well.

Introducing Stemmer

Something I discovered as I was building Simple Bayes, is something called stemming. Let’s see another example:

SimpleBayes.init(stem: false)
|> SimpleBayes.train(:apple, "buying apple")
|> SimpleBayes.train(:banana, "buy banana")
|> SimpleBayes.classify("buy apple")
# => [
# banana: 0.057143654737817205,
# apple: 0.057143654737817205
# ]

Oops, the probabilities of the sentence “buy apple” of being apple and banana are the same, that’s not good, as we know “buying” and “buy” should mean the same thing. This is where stemming comes in handy. Let’s enable stemming and run the example again:

SimpleBayes.init(stem: true)
|> SimpleBayes.train(:apple, "buying apple")
|> SimpleBayes.train(:banana, "buy banana")
|> SimpleBayes.classify("buy apple")
# => [
# apple: 0.18096114003107086,
# banana: 0.1505149978319906
# ]

Stemming in this case correctly identified “buy” and “buying” as the same thing (they both have the stemmed root of “buy”).

How did I include stemming in Simple Bayes you wonder? Let me introduce you to Stemmer - as of writing this is the only Porter2 stemmer available in Elixir. :)

Elixir and BEAM may not be known for their raw performance, but compared to a similar Porter2 stemmer implemented in pure ruby, my Elixir version runs about five times faster (under 5s to stem 29000+ words, compared to 25s with the ruby version).

The Learning Continues…

I’ve only been writing Elixir for a month, and there are still heaps to learn and to practice. So far though, I have to say my experience with both Elixir and Phoenix has been fantastic - I get the same satisfying and pumped feeling I got when I was learning Ruby and Rails years ago.

If you haven’t checked out Elixir, I strongly encourage you to do so, after all, the world seems to be moving in the concurrency direction at an increasingly rapid speed, including Ruby 3x3. :)

]]>

Developers, Being Treated Poorly? You Are Not Alone!

2016年4月5日 19:40
UPDATE: Reading Hacker News comments it is apparent to me that I have not explained my position and intention very well. Please allow me to clarify - I do not resent the people involved in these moments, if anything, I thought those encounters were good learning experience for me, as I know there are things I would do and say differently so others around me won’t feel I’ve been too harsh on them. I did not share these moments to complain, but to raise the awareness that kinder communication styles might be better received.

As a software professional who’s been in the industry long enough to have accumulated quite a few battle scars, I tend not to think back about the unpleasant, unfair and sometimes bizarre moments that make me feel depressed or angry. If there is one thing I learnt over the years, it’s to let go and move on.

Despite the effort in staying positive, I still believe it is valuable to share these moments. If you work in software and feel like you are being treated poorly, you are definitely not alone. By openly sharing these moments, I am hoping more people will start taking notice, and perhaps will lend a hand or a shoulder when a colleague is in need of support.

So, I’d like to share a few moments that have happened in my career, in no particular order. There are a few of these moments that I still find hard to swallow, but most of them I simply chuckle whenever I think about them. ;)

One morning I came to work, noticed a few server alerts that required attention. This was pre AWS and the whole Devops automation movement days so I needed to log onto the server. For some reason my access was revoked so I had to log into the hosting dashboard to reset the root password. The senior dev on the team turned up, and when asked about my access, casually told me: “oh you did something on the servers the other day, so I removed your access.”


CTO asked me to help building a new mission critical PHP and MySQL service, upon discovering an existing service already built on Ruby and MongoDB by three very senior developers and consultants, CTO said the reason for the rebuild was because “ActiveRecord is too slow.” Let me remind you that the initial Ruby solution was built using MongoDB. At the time the company had about 30-40 experienced Ruby developers, and only 2 junior to mid level PHP developers who were specifically hired to build the PHP/MySQL service which turned out to be a disaster.


As the team lead who was responsible for evaluating performance and setting salary, I was told by one of my team members that they had gotten a pay rise. Surprised, I asked one of the founders of the company who is non-technical what was going on, during the conversation I discovered another pay rise was given to one other member of my team - all without consulting or even informing me.


Excited to have had my overseas conference talk proposal accepted, I asked the new head of our department who had joined our company for only a month or two for travel approval. Fully expected a pad on the back, I was surprised to have been told that I cannot go, with the reason being “it is just not a good time”.


One of our servers needed to be rebuilt, so trying to be a team player and help, I started installing some basic packages. The senior dev on the team turned to me, straight faced and enunciated in a deep and cold voice: “don’t touch anything on it, this is my server!”


The new development manager walked into our meeting, pulled me out, guided me to another meeting room where the new CTO was waiting. The new CTO opened with “so it’s time for us to go separate ways”, and implied the reason being something that I could definitely have pushed for unfair dismissal. Trying to be professional, I walked back into my original meeting, but the new development manager quickly pulled me out and said “you need to go right now”. He then stood behind me, saw me formatted my work laptop, then literally escorted me out of the office. The new development manager and CTO soon drove the company to the ground and left the country.


After submitting the salary increases for the all my direct reports, I was delighted that the CEO was happy with them all. Given I have had no pay increases in over one and half years I asked for a modest pay increase for myself. I was told “no, we have to discuss this after your project delivery.” My direct reports and I were in the same delivery team.


It was near the end of the working day, around 5pm, as the person coordinating the developer recruitment in our area I ping’ed our Slack channel with a lighthearted message to encourage our developers to start reviewing some code tests from job candidates if they’re free. One person replied: “My end of day activity is doing the stuff I should have been doing all day instead of the other things that came up.”


One day I got really really sick - difficult to walk or even stand still, dizzy and having fever. Upon informing one of the founders, I was told to come into the office, even though there was no urgent tasks need to be done. Reluctantly obliged, I came into the office, went out for lunch with colleagues but I was so sick I could not eat anything and can barely sit straight. I then went home, and was sick for the rest of the week…


My manager, who was the general manager, wanted to fire two of my developers. His tactic for firing the senior developer was to make the senior developer role redundant and offering the dev a junior developer role instead.


I was tasked to investigate setting up a Cassandra database cluster. Upon discovering certain network limitations, I approached the then head of network operations, when asked about the inability to access the Cassandra cluster, he replied confidently that “it works”. Later on I discovered that he successfully telnet’ed to the ports and declared it “working”.


The head of HR who is female and a big advocate for workplace diversity walked into my exit interview. The opening line was “Ordinarily I only do exit interviews with female employees…”


New to the big corporation environment, after asking around a few people and could not figure out how to provision an iPad for my team (but knowing some other teams do have company iPads), in an email reply by the head of enterprise IT: “Hey Gents, Fred has escalated this through multiple channels today. Awesome effort on his behalf, to seems like he didn’t like our response.” To which a colleague had to gently remind him that it wasn’t my fault there was no documented process for provisioning hardware.


After having some discussions on our MySQL database, the dev who had recently joined my team all of a sudden raised his voice in an extremely arrogant and dismissive tone: “you clearly don’t understand this feature, do you?”


A senior software architect who I used to respect walked close by in a meetup. We worked for the same company a while back so I smiled, said hi and was about to start a conversation, he quickly cut me off with “I need to get a drink” without looking at me and wondered off.


The general manager who is non-technical, asked me to investigate options to uplift our ageing bespoke ecommerce solution. Upon delivering my findings, I was told that “your findings are biased.” Later it was clear to me that he wanted confirmation of his agenda from the technical perspective and his mind had already been made on the approach we should take.


A new big shot executive joined the company as our new CTO. He had no agile background and our company was transforming and pushing for a lot of agile principles at the time. Two weeks into his new appointment, the new CTO published an internal document titled “Controlled Chaos”. After reading the document everyone immediately realised that he was describing waterfall. The document was shared as a Google Doc and was open for comments, so people started asking hard questions. Weeks later, many of us who were vocal about his document were let go.

There you are, these were my moments. What were yours? Care to share? :)

]]>

Leading Snowflakes by Oren Ellenbogen - A Pragmatic Book on Software Team Leadership I Wish I Read Sooner

2014年7月26日 23:49
Two weeks ago as I was reading Software Lead Weekly which I had subscribed to for a while, I discovered its curator, Oren Ellenbogen’s book - Leading Snowflakes.

It was a moment of discovery that lead to a stream of delightfulness.

It Started with a Long Day at Work…

After a long day at work, I was so beat I couldn’t even listen to audio books like I always do on my way to and from work. So I drove home that night in total silence. One thing that was on my mind at the time was - who should I reach out to for some advice and mentorship?

“Probably no one.” I shrugged, and decided to simply suck it up and sleep it off, hoping solutions to the problems I had would come to the light of the day.

I got home, opened up my laptop and started reading unread emails. “Software Lead Weekly, ah I haven’t read the latest issue yet,” I thought to myself, “I wonder who the curator is.”

Discovered “Leading Snowflakes”

And that had lead me to discover Oren Ellenbogen’s book Leading Snowflakes.

On the book’s landing page it says:

This isn’t another 350-page book you’ll agonize yourself over not reading. It’s short, concise and pragmatic. You’re busy, I get that.

“This is great!” I thought, with a sense of uncontrollable excitement. After all, these days I found myself hard to justify spending hours and hours on books that talk about subjects in a hand-wavy manner. A short, concise and pragmatic book on leadership was exactly what I needed.

There was a free sample chapter for download, so I clicked on it without hesitation. To my surprise, after downloading the sample chapter, Oren reached out to me with a personal email, and that had quickly lead to a chain of emails of me writing about the things at work that I found challenging, and Oren providing sound advice and suggestions.

I felt not only grateful, but lucky. Over the years I had worked at varies places where I was fortunate enough to have met quite a few people who I consider my mentor. But to run into one who I had never met before, on the same night my spirit was crushed, and who were willing to not only listen to my rant but also to walk me through my problems and offer advice - it was like finding a needle in a haystack.

My decision to purchase the book was made half way through our email exchanges, as I was browsing through the links Oren provided on his book landing page (under the section of “Sharing my lessons learned”), I found them to be very insightful.

Convinced that Oren’s book would offer even more value, I quickly bought the complete package with the book and all 10 interviews.

Leading Snowflakes - A Book Every Software Team Manager Should Read

Fast forward to today, I have finally finished reading the book, and I could confidently say that this is a book every software team manager should read.

There is no hand-wavy theories or lectures, what the book offers is exactly what is said on the landing page - short, concise and pragmatic.

The book contains a lot of sound advice, practical tools and techniques as well as a ton of useful links. Reading through it, I can recall many moments where I thought to myself, “hey, that indeed is a very cool technique, I can totally see how I could use it and be better at leading.”

I am now ready to implement those techniques and I am hopeful that I would become a better team leader and my team would become a better team as a result. :)

If you are a software team manager, or aspire to be one, I highly recommend this book!

To purchase only the ebook itself, Oren has kindly offered a 20% off discount, click to buy now.

]]>

Photos from JSConf AU 2014

2014年4月10日 20:09
JSConf AU has been fun - great talks and great venue! It was also a wonderful opportunity for me to test out my new camera kit: SONY A7r + Sony Sonnar T* FE 35mm f/2.8 ZA + Voigtlander Color Skopar 20mm f/3.5 SL-II with Novoflex SONY E-Mount to Nikon adapter.

Here’s the kit (taken by Nikon D800 + Tamron SP 24-70mm F/2.8 Di VC USD):

Photos from JSConf AU 2014:

Breakfast in North Melbourne:

Conference:

Check out my Google+ Photo Album for larger versions.

]]>

Protip: Unsync the "Index" Folder of Sublime Text 3 from Dropbox

2013年10月12日 12:26
If you’re like me who uses both Sublime Text 3 and Dropbox, chances are you have your Sublime Text 3 folder synced in Dropbox.

I use my laptop as my primary workstation so most of the time it’s docked and charged. Occasionally when I do use it on battery power however I notice the extremely poor battery life - typically only 2-3 hours.

Eventually I realised the power consumption was caused by Sublime Text 3 generating a temp file in its “Index” folder every second or so, and that triggers Dropbox to do the syncing - causing it to take 40-50% of CPU constantly.

So, here’s a simple fix. Click on the Dropbox icon, then click on the gear icon and select “Preferences…”, select the “Advanced” tab, and select “Change Settings…” for Selective Sync to bring up the column-based file dialog, now simply find and untick the “Index” folder and you’re all set. :)

]]>

Protip: Faster Ruby Tests with DatabaseCleaner and DatabaseRewinder

2013年9月18日 14:50
Please also see this blog post on tweaking your ruby GC settings.

I use and love DatabaseCleaner, although historically I had never paid too much attention on the performance of its varies cleaning strategies - I’d always used truncation.

We use Postgres, and after digging around and finding out the difference between DELETE and TRUNCATE, I ended up improving our test suite speed by about 30-40% simply by tweaking the cleaning strategies.

RSpec.configure do |config|
  config.before :suite do
    DatabaseCleaner.clean_with :truncation
    DatabaseCleaner.strategy = :transaction
  end

  config.before do
    if example.metadata[:js] || example.metadata[:type] == :feature
      DatabaseCleaner.strategy = :deletion
    else
      DatabaseCleaner.strategy = :transaction
      DatabaseCleaner.start
    end
  end

  config.after do
    DatabaseCleaner.clean
  end
end

Essentially, we want to truncate the DB only once before the whole suite runs to ensure a clean slate DB, then we only want to use deletion on Capybara tests, everything else should just use transaction which is the fastest strategy.

Now, as a bonus, I have just discovered @amatsuda’s DatabaseRewinder which is a lightweight alternative that supports only ActiveRecord. It offers comparable performance with a much similar API.

RSpec.configure do |config|
  config.before :suite do
    DatabaseRewinder.clean_all
  end

  config.after do
    DatabaseRewinder.clean
  end
end

By the way, we also use parallel_tests to scale our test suite to multiple processes, even on Travis CI and Wercker.

Hooray to faster tests! :)

]]>

Protip: Ruby Devs, Please Tweak Your GC Settings for Tests!

2013年9月6日 19:13
It was made apparent to me that many ruby devs either aren’t aware or couldn’t be bothered to tweak their ruby garbage collector settings.

Well, if you are using MRI, please start tweaking your GC settings. Here’s what I use (on my 15” Macbook Pro Retina):

export RUBY_GC_MALLOC_LIMIT=90000000
export RUBY_FREE_MIN=200000

Not only can you tweak it for your local dev machine, you can also tweak Jenkins, Travis CI, Wercker and other CI solutions, making instant speed gain for your test suite!

Here’s what we get:

            Before After
Local:      8m22s 5m52s
Travis CI:  10m10s 6m0s
Wercker:    8m45s 6m24s

YMMV depending on your system and available RAM.

]]>

Datamappify - A New Take on Decoupling Domain, Form and Persistence in Rails

2013年6月27日 20:09
This post is about the ruby library we are building - Datamappify, please go check it out.

At Locomote we are building a relatively large web application using Rails. Before we began to lay the foundation, we knew very well that if we wanted the project to be maintainable we had to architect the system with extra care and attention. More specifically, we can’t rely on simply using ActiveRecord which combines behaviour and persistence as our domain models.

We began our search for something that would help us decouple our application from the domain layer down to the form handling. We’ve found a couple of gems that are close to what we were after - Curator, Minimapper, Edr and later on Reform. They are all wonderful gems but unfortunately none of them has everything we need.

Here are the things we need:

  • Decouple domain logic and data persistence.
  • Decouple models and forms.
  • Support ActiveRecord (or at the very least, ActiveModel) so we can still use many of the awesome gems like Devise, SimpleForm and CarrierWave.
  • Support attributes mapped from different data sources (e.g. remote web services from third-party vendors).
  • Support lazy loading so that attributes stored on remote data sources will not get triggered upon loading the entities.

All things considered, we bit the bullet and started working on Datamappify.

Before I go into details and examples, here is an extremely simplified overview of Datamappify’s architecture:

As you can see, Datamappify is loosely based on Repository pattern and Entity Aggregation pattern.

Datamappify has three main design goals:

  1. To utilise the powerfulness of existing ORMs so that using Datamappify doesn’t interrupt too much of your current workflow. For example, Devise would still work if you use it with a UserAccount ActiveRecord model that is attached to a User entity managed by Datamappify.
  2. To have a flexible entity model that works great with dealing with form data. For example, SimpleForm would still work with nested attributes from different ORM models if you map entity attributes smartly in your repositories managed by Datamappify.
  3. To have a set of data providers to encapsulate the handling of how the data is persisted. This is especially useful for dealing with external data sources such as a web service. For example, by calling UserRepository.save(user), certain attributes of the user entity are now persisted on a remote web service. Better yet, dirty tracking and lazy loading are supported out of the box!

You can read more about Datamappify in the project’s README. For this blog post I will focus on how we use Datamappify in our Rails application. We are still early days in our application development and Datamappify still has quirks and issues, but I am hoping this post will illustrate some of the key benefits of Datamappify.

Getting Started

Getting started with using Datamappify is really easy. We simply include it in the Gemfile:

gem 'datamappify'

To keep things organised, we put entities and repositories in their respective directories under app:

Entities

The reason we wanted Datamappify to utilise existing ORMs like ActiveRecord, is so that we could still use gems like Devise.

So, we have a UserAccount model that handles authentication:

class UserAccount < ActiveRecord::Base
  devise :database_authenticatable,
          :recoverable, :rememberable, :trackable, :validatable
end

UserAccount has one and only one purpose - user account authentication. Other user behaviours would be contained in either the User entity itself or service objects. Speaking of the User entity, it looks like this:

class User
  include Datamappify::Entity

  attr_accessor :user_account

  attribute :account_username, String
  attribute :account_email, String
  attribute :first_name, String
  attribute :last_name, String
  attribute :activated, Boolean, :default => true

  attributes_from Contact, prefix_with: :work

  validates :first_name, presence: true
  validates :last_name, presence: true

  references :agency

  def full_name
    "#{first_name} #{last_name}"
  end
end

We include Datamappify::Entity in the User class to make it an entity. We set the :user_account accessor is so that we could attach the UserAccount object onto the entity.

The attribute DSL is from Virtus - we get attribute type coercion for free, awesome!

attributes_from is a DSL provided by Datamappify - it essentially “mounts” all the attributes from another entity, in this case the Contact entity, which looks like this:

class Contact
  include Datamappify::Entity

  attribute :email, String
  attribute :phone_number, String
  attribute :fax_number, String

  validates :phone_number, presence: true
  validates :fax_number, presence: true
end

All the attributes and validation rules from Contact are now “mounted” on User. attributes_from Contact, prefix_with: :work is equivalent to:

attribute :work_email, String
attribute :work_phone_number, String
attribute :work_fax_number, String

validates :work_phone_number, presence: true
validates :work_fax_number, presence: true

Validation DSL is provided by ActiveModel::Validations.

references :agency is a convenient DSL provided by Datamappify. It is equivalent to:

attr_accessor :agency

attribute :agency_id, Integer

Now that we have entities, we need a way to retrieve and store them. For that we need repositories.

Repositories

Here is the user repository:

class UserRepository
  include Datamappify::Repository

  for_entity User

  default_provider :ActiveRecord

  map_attribute :account_username, 'ActiveRecord::UserAccount#username'
  map_attribute :account_email, 'ActiveRecord::UserAccount#email'

  map_attribute :work_email, 'ActiveRecord::Contact#email'
  map_attribute :work_phone_number, 'ActiveRecord::Contact#phone_number'
  map_attribute :work_fax_number, 'ActiveRecord::Contact#fax_number'
end

Similarly to an entity, we include Datamappify::Repository to make the class a repository. We specify for_entity to link the repository to an entity, and default_provider to use a specific data provider for unmapped attributes.

Unmapped attributes are the ones not specified in map_attribute, in this case they are first_name, last_name and activated. Unmapped attributes are actually automatically mapped by Datamappify, so the user repository essentially does this:

map_attribute :first_name, 'ActiveRecord::User#first_name'
map_attribute :last_name, 'ActiveRecord::User#last_name'
map_attribute :activated, 'ActiveRecord::User#activated'

The first argument of map_attribute is the name of the attribute from the User entity (e.g. :first_name).

The second argument is a string containing three things:

  1. ActiveRecord is the data provider.
  2. ::User is the ActiveRecord model class.
  3. #first_name is the ActiveRecord attribute from the model class.

Even though the User entity is a representation of a user on the domain level, the underlying data structure does not necessary have to be. As you can see from the user repository example, we are mapping :account_username and :account_email to the UserAccount ActiveRecord model we’ve seen before. And we have a bunch of contact details attributes mapped to the Contact ActiveRecord model.

The database schema therefore looks like this:

create_table "contacts", force: true do |t|
  t.string "email"
  t.string "phone_number"
  t.string "fax_number"
  t.integer "user_id"
end

create_table "user_accounts", force: true do |t|
  t.string "username", default: "", null: false
  t.string "email", default: "", null: false
  t.string "encrypted_password", default: "", null: false
  t.string "reset_password_token"
  t.datetime "reset_password_sent_at"
  t.datetime "remember_created_at"
  t.integer "sign_in_count", default: 0
  t.datetime "current_sign_in_at"
  t.datetime "last_sign_in_at"
  t.string "current_sign_in_ip"
  t.string "last_sign_in_ip"
  t.integer "user_id"
  t.datetime "created_at"
  t.datetime "updated_at"
end

create_table "users", force: true do |t|
  t.string "first_name"
  t.string "last_name"
  t.boolean "activated"
  t.integer "agency_id"
end

Note that because the Contact entity is mounted on the User entity, we need a foreign key user_id in the contacts table to link them.

Data providers

Because we are allowed to specify a data provider (i.e. ActiveRecord) for each attribute, we can map attributes to entirely different data providers! For instance, we could have:

map_attribute :first_name, 'ActiveRecord::User#first_name'
map_attribute :last_name, 'ActiveRecord::User#last_name'
map_attribute :activated, 'Sequel::UserActivation#activated'

Now the activated attribute is mapped to the UserActivation Sequel model. This powerful feature would allow us to develop data providers that communicate with remote web services. :)

Repository APIs

Datamappify provides a bunch of APIs for retrieving and storing data. These APIs are being developed as needed during our application development.

For instance, to find a particular user entity by ID:

user = UserRepository.find(1)

To get all the users:

users = UserRepository.all

To search for users:

users = UserRepository.find(:first_name => 'Fred', :activated => true)

To save a user:

UserRepository.save(user)

To delete a user:

UserRepository.destroy(user)

There are some limitations for certain APIs, you can read more about them here.

When saving an entity, because Datamappify has an internal pool for tracking dirty attributes, only those dirty attributes will get saved.

Also, Datamappify supports attribute lazy loading, all we have to do is to tell our entity to become lazy aware:

class User
  include Datamappify::Entity
  include Datamappify::Lazy
end

Once an entity is lazy aware, a repository will inject the lazy loading mechanism onto the entity when it retrieves such entity (via find).

Both repositories and entities support inheritance, again, you may read more about them in the project’s README.

Putting Things Together With A Controller

Remember we have the UserAccount for authentication? So how does that work? Well, here’s the piece of code in our application controller:

class ApplicationController < ActionController::Base
  before_filter :authenticate_user_account!

  private

  def current_user
    unless current_user_account.blank?
      @current_user ||= UserRepository.find(current_user_account.user_id)
    end
  end

  helper_method :current_user
end

Not too different from the normal Devise workflow hey? ;)

Forms

Web applications typically have lots of forms - ours is no different. It turns out, Datamappify can help build form objects too!

Here’s the view portion of one of our forms (we use Slim templates):

# our standard form layout

= simple_form_for submission_target, url: submission_path, signed: true do |f|

  = yield(f)

  .form-actions
    .pull-left
      - if archivable? && resource.persisted?
        = link_to 'Archive', '#', method: :delete, class: 'btn-archive'

    = f.submit 'Save'
    = link_to 'Cancel', cancel_path

# actual form content

= render layout: 'forms/resource' do |f|
  h3 Account Details
  .row-fluid
    .span4 = f.input :account_type, collection: BankAccount::ACCOUNT_TYPES.invert
    .span4 = f.input :account_code
    .span4 = f.input :account_name
  .row-fluid
    .span4 = f.input :account_number
    .span4 = f.input :bank_name
    .span4 = f.input :bank_branch
  .row-fluid
    .span4 = f.input :bank_bsb, label: "Bank BSB"
    .span4 = f.input :activated, as: :boolean

  h3 Branch Details
  .row-fluid
    .span4 = f.input :branch_address_line_1, label: "Address 1"
    .span4 = f.input :branch_address_line_2, label: "Address 2"
    .span4 = f.input :branch_address_line_3, label: "Address 3"
  .row-fluid
    .span4 = f.input :branch_state, label: "State"
    .span4 = f.input :branch_postcode, label: "Postcode"
    .span4
      = f.input :branch_country_code, label: "Country" do
        = f.country_select :branch_country_code, ['au', 'us', 'gb'], value: 'au'
      - f.add_signed_fields :branch_country_code
  .row-fluid
    .span4 = f.input :branch_phone_number, label: "Phone number"
    .span4 = f.input :branch_fax_number, label: "Fax number"

  h3 Merchant Details
  .row-fluid
    .span4 = f.input :merchant_number
    .span4 = f.input :merchant_name
    .span4 = f.input :merchant_address
  .row-fluid
    .span4 = f.input :merchant_biller_code, label: "Biller code"
    .span4 = f.input :merchant_bpay_method, collection: Merchant::BPAY_METHODS.invert, label: "BPAY method"

  h3 Payment Details
  .row-fluid
    .span4 = f.input :payment_number
    .span4 = f.input :payment_merchant, label: "Merchant description"
    .span4 = f.input :payment_reference_type, collection: PaymentAccount::REFERENCE_TYPES.invert, label: 'Reference type'

As you can see, it’s a form containing details from bank account, branch address, branch contact information, merchant and payment. And indeed we do have bank_accounts, addresses, contacts, merchants and payment_accounts tables in our database. Yet, the form still remains flat-structured and submits via simple_form_for.

This is thanks to our BankAccount entity and BankAccountRepository repository:

class BankAccount
  include Datamappify::Entity
  include Concerns::Archivable

  ACCOUNT_TYPES = {
    'general' => 'General',
    'trust' => 'Trust'
  }

  attribute :account_type, String
  attribute :account_code, String
  attribute :account_name, String
  attribute :account_number, String
  attribute :bank_name, String
  attribute :bank_branch, String
  attribute :bank_bsb, String
  attribute :activated, Boolean, default: true

  attributes_from Address, prefix_with: :branch
  attributes_from Contact, prefix_with: :branch
  attributes_from Merchant, prefix_with: :merchant
  attributes_from PaymentAccount, prefix_with: :payment

  references :agency

  validates :account_type, Mos::Validation.in(ACCOUNT_TYPES)
  validates :account_code, Mos::Validation::STANDARD_TEXT
  validates :account_name, Mos::Validation::STANDARD_TEXT
  validates :account_number, Mos::Validation::STANDARD_TEXT
  validates :bank_name, Mos::Validation::STANDARD_TEXT
  validates :bank_branch, Mos::Validation::STANDARD_TEXT_OPTIONAL
  validates :bank_bsb, Mos::Validation::BSB
  validates :merchant_bpay_method, Mos::Validation.presence_depend_on(:merchant_biller_code)
  validates :payment_reference_type, Mos::Validation.presence_depend_on(:payment_number)
end

class BankAccountRepository
  include Datamappify::Repository

  for_entity BankAccount

  default_provider :ActiveRecord

  map_attribute :branch_address_line_1, 'ActiveRecord::Address#address_line_1'
  map_attribute :branch_address_line_2, 'ActiveRecord::Address#address_line_2'
  map_attribute :branch_address_line_3, 'ActiveRecord::Address#address_line_3'
  map_attribute :branch_state, 'ActiveRecord::Address#state'
  map_attribute :branch_postcode, 'ActiveRecord::Address#postcode'
  map_attribute :branch_country_code, 'ActiveRecord::Address#country_code'

  map_attribute :branch_phone_number, 'ActiveRecord::Contact#phone_number'
  map_attribute :branch_fax_number, 'ActiveRecord::Contact#fax_number'
  map_attribute :branch_email, 'ActiveRecord::Contact#email'
  map_attribute :branch_website, 'ActiveRecord::Contact#website'

  map_attribute :merchant_number, 'ActiveRecord::Merchant#number'
  map_attribute :merchant_name, 'ActiveRecord::Merchant#name'
  map_attribute :merchant_address, 'ActiveRecord::Merchant#address'
  map_attribute :merchant_biller_code, 'ActiveRecord::Merchant#biller_code'
  map_attribute :merchant_bpay_method, 'ActiveRecord::Merchant#bpay_method'

  map_attribute :payment_number, 'ActiveRecord::PaymentAccount#number'
  map_attribute :payment_merchant, 'ActiveRecord::PaymentAccount#merchant'
  map_attribute :payment_reference_type, 'ActiveRecord::PaymentAccount#reference_type'
end

Pretty clean and straightforward. What do you think?

Datamappify - Keeping Your Domain Layer Sane

Datamappify’s concept isn’t new, but there simply isn’t anything in the ruby community that solves everything Datamappify tries to solve.

I hope that this post has not only introduced you to Datamappify, but has also made you think about how to make your application’s domain layer more decoupled.

Thanks for reading, and if you have any feedback for Datamappify please get in touch with me!

]]>

An Interview with Yukihiro "Matz" Matsumoto

2012年11月25日 14:45
Update: Please check out this new interview with Matz, done by Engineer Type.

A week ago I went to Shanghai, China to attend and to give a talk at RubyConf China. The day before the conference’s first day a bunch of us were invited to a VIP dinner where we met with Matz and got to play with a device running MRuby. And I heard that earlier on that day Matz was ‘adopted’ by a book publisher to do an interview.

I have found the interview (in Chinese), and found it to be really useful. So I translated it to English. Hope more people will like it. :)

On November 16th, 2012 - a day before RubyConf China, a Chinese book publisher Turing Book has done an interview with Matz on his new book The Future of Computing as well as about a few topics interested to Chinese readers. The interview was conducted by The Future of Computing’s Chinese translator Zi Heng Zhou.

Zhou: Mr. Matsumoto’s new book The Future of Computing is published earlier this year and the Chinese version is being translated by yours truly, and that is going to be released some time next year in China. Your last book The World of Code has received high praises amongst the readers in China, so what are the differences between the last and the new book?

Matz: The World of Code has 14 topics in total, and each topic covers the basics - they covered more breadth than depth. The new book on the other hand has one set topic - thoughts on the emerging technologies in the future, therefore the ground covered would be narrower and more in depth than the last book. On top of that, the new book discusses several things by timescale, such as the history and the changes since the invention of computing, and computing’s impact on our future lives. Therefore it’s the thoughts of both the past and the future. The computing world is changing rapidly, and this book’s purpose is to discuss the direction of computing heading into the future.

Zhou: Speaking of the history of computing, you have touched on a few things about Moore’s law in the book?

Matz: Moore’s law describes the rule of changes over the history of computing hardware. The book discusses not only the changes on computing itself, but also the changes on its surrounding environment.

Zhou: On the topic of the evolution of programming languages, Paul Graham said in The Hundred-Year Language that the main branches of the evolutionary tree pass through the languages that have the smallest, cleanest cores. In the new book you seem to hold a different opinion, can you tell us why? And what’s your take on the evolution of programming languages?

Matz: Paul loves Lisp, and Lisp perfectly matches the characteristics of the programming languages described in his essay, and so Paul reckons the programming languages in a hundred years from now will look like Lisp. In reality though, Lisp has been around for over 50 years, and to be honest it isn’t one of the mainstream programming languages. In my opinion this may have been because most programmers don’t find Lisp charming enough. In other words, there is a gap between the so-called “smallest, cleanest cores”, “beautiful” languages and the expectation of programmers. It would be understandable if Lisp’s charm had not been accepted by everyone in a year or two, but for 50+ years it hasn’t reached the mainstream, could it be because it fundamentally does not match our expectation? There is a huge difference between human friendly languages and languages that have smallest, cleanest cores , and I am afraid the gap between them might not close even in a hundred years. As for what future programming languages should look like, I think they should have a runtime model similar to that of Lisp and be easily understandable by humans. All of a sudden, Ruby looks a lot closer to that, doesn’t it?

Zhou: Mr. Matsumoto you are the father of Ruby. We know that during the design of a programming language there might be a lot of different choices to be made, e.g. dynamically typed vs statically typed, prototype based vs class based, etc. When you were designing Ruby, what was the most difficult choice you made?

Matz: Before Ruby I actually have designed another language when I was in university, and that language was statically typed, similar to Eiffel. I liked statically typed languages, but the language I designed during university was more for academic purposes. Several years later, when I wanted to design a language as a tool I could use, I preferred to have it dynamically typed as I think they were more practical. So I designed Ruby. And I think it was the correct decision - it might not have been the most difficult decision, but it certainly was the biggest decision. Now that the decision of being a dynamic language is made, languages such as Smalltalk, Lisp and to an extent Perl have all had influences on Ruby. One of the features of Ruby is “mixins”, and mixins were not very common at the time Ruby was created. But because I don’t like multiple inheritance, I always believed that there must be an easier way to achieve similar results, so I designed mixins in Ruby.

Zhou: Looking back, is there anything in Ruby you wish you did differently?

Matz: In the beginning my goal was to replace Perl as my tool therefore I have borrowed many ideas from Perl, such as using the dollar sign ($) to indicate special variables. Looking at it now it seems to be a little bit too much and too similar to Perl. There are a few other things but mainly I think it is too similar to Perl. Back then before the ruby idioms were formed, there were many things that were borrowed from Perl - nowadays I think many of them weren’t necessary thanks to the ruby and rails idioms.

Zhou: Many believe Ruby’s popularity was because of Ruby on Rails, and you agreed with this in the book. So what do you think made Rails so successful?

Matz: The first and foremost, it benefitted from the rapid growth of the web - almost every software development platform is eyeing on the web field. What was developed using the traditional Client-Server architecture can now be implemented on the web. The number of applications that can be developed for the web has grown, and that’s important. Secondly, Ruby is optimised for easier development and higher development productivity. I think these two reasons combined, is what made Rails so successful.

Also, Ruby has many powerful features such as meta-programming and extendability via monkey patching. Through these features, basic classes can be enhanced, and DHH created Rails using these powerful features of Ruby. For people who have never touched Ruby, for example those Java programmers who like “inflexible” languages, they will be like, “Huh? You can do that?” - and I think that’s also one of the reasons why Rails is so popular.

Zhou: It is said that DHH was going to create a web framework using PHP, but eventually moved to Ruby?

Matz: Yes, and I think the reason for that might be the limitation of meta-programming in PHP. After Rails was released there were a bunch of PHP frameworks came out that are inspired by Rails, such as Symfony and CakePHP. PHP however does not have many of the powerful features found in Ruby, and purely from the development perspective I still believe Rails is the more powerful one out of the bunch. By the way, I actually met with DHH before, in Denmark. Back then he hadn’t started learning Ruby yet, perhaps that had a small impact on him too.

Zhou: Ruby is being used widely for the web, but one hot topic amongst Chinese readers is the development direction of Ruby outside of the web. Any thoughts?

Matz: True, many web based projects use Ruby, for instance web frameworks like Rails and Sinatra, etc. The world of programming however is far beyond simply web, and I have always wanted Ruby to break out from the web to other areas. In the near future, I would like to see Ruby being used in three fields:

1.

Scientific computing. In this field, languages like Python, R and Matlab are popular choices. I hope Ruby will soon be one of them. In fact, we are developing a project called SciRuby, and we hope it will help us with the adoption in scientific computing.

2.

High performance computing. This is somewhat related to scientific computing - to use super computers for doing computational work. Compared to C++, Ruby indeed is very slow, and that’s why people think Ruby isn’t suitable for doing high performance computing. In University of Tokyo a research student is working on an academic research project that compiles Ruby code to C code before compiling the binary code. The process involves techniques such as type inference, and in optimal scenarios the speed could reach up to 90% of typical hand-written C code. So far there is only a paper published, no open source code yet, but I’m hoping next year everything will be revealed.

3.

Embedded systems. For micro devices such as cell phones, medical equipment and robots, Ruby isn’t really suitable due to its memory use and APIs. Therefore we need something that’s more suitable for these embedded systems where memory consumption has to be low and APIs have to be optimised. And that’s what MRuby is designed for.

Zhou: Twitter is mainly built using Rails, but I read a news recently about the traffic surge during the US president election period and Twitter is now migrating to other platforms to help with the scaling. What’s your view on this?

Matz: There are many reasons I believe. First of all, nobody could have predicted Twitter’s huge success - who would’ve thought a service that essentially provides 140 characters of text for blogging to become one of the largest social networks. During their rapid growth they have added a lot of new features, and I think Ruby has contributed to that - it allowed many new features to be thought and implemented in very short amount of time. Twitter could not have anticipated the traffic it is now handling, so it’s fair to say that they have now hit the limit of their architecture designed from way back.

To solve the issue, Twitter needs to design a new architecture from the ground up in order to handle the ever increasing traffic. Although, Ruby can still be used for rewriting the architecture (chuckles). More seriously though, when a platform hits its limit, there are a few ways of solving it such as rewriting the architecture and changing to another language/platform, etc. I believe the most effective way is to rewrite the architecture, and this is exactly what Twitter’s been doing. During the rewriting process the Twitter engineers wanted to take on more challenges so they picked Scala. Because Scala is a compiled language it has great performance, so it is a fine choice for the new architecture.

My opinion is that when your system is still in its growing stage, it is far more important to have the ability to react quickly to changes, and that’s what a highly flexible language such as Ruby offers. Once your system reaches to a point of maturity, stability and success, then to have a new architecture that saves on resources makes sense. Twitter only chose to use Scala for its core components, the web front-end and many of their internal tools are still using Ruby. As a matter of fact, I paid Twitter a visit last month and talked to many of their engineers there - Ruby is still in great use (chuckles).

Zhou: Mobile platforms become hotter and hotter in recent years due the increased use of smartphones, tablets and other mobile devices. As far as programming language is concerned, Android uses Java and iOS uses Objective-C. What about scripting languages like Ruby, how do you see them fit in this picture?

Matz: It is no doubt that so far in order to develop for Android you use Java, and for iOS you use Objective-C. But that also creates a huge barrier because you would need to rewrite your application if you wanted to port from one platform to another. Projects like PhoneGap and Titanium try to solve this very issue, via languages such as JavaScript and Lua to offer cross-platform compatibility. For Ruby, there is a project called Rhodes - you can use Ruby to write applications that will run on iOS, Android and Blackberry.

In the old days, mobile devices are much less powerful than PCs, and if applications can’t run in full speed then they are useless. Nowadays though, mobile devices have gotten significantly faster, and the use of cross-platform frameworks becomes far more practical than ever before. I think this way of developing for mobile platforms will become more and more popular. And by the way, we spoke about MRuby before, and using MRuby for iOS and Android is already underway, hopefully more applications will get developed using MRuby in the near future.

Zhou: You mentioned MRuby, and the topic of your keynote presentation tomorrow is Ruby 2.0. Could you talk about MRuby and Ruby 2.0’s highlights a little bit?

Matz: As we discussed earlier, MRuby is designed to run on embedded devices, it does not have everything from Ruby. As a result, many devices which traditionally can’t use Ruby, such as vendor machines, controllers and robots can soon utilise MRuby. Perhaps in a few years time, we could see Ruby used in televisions and cars.

Ruby 2.0 indicates two messages. Firstly, by next year it will be 20 years since the development of Ruby started. What better version number can there be to celebrate this special moment! Contrary to what it might seem like, in terms of the changes, from 1.9 to 2.0 is not as big as from 1.8 to 1.9. In fact, our goal for 2.0 is to be fully backward compatible with 1.9. Your 1.9 applications should run just fine on 2.0.

Secondly, Ruby 2.0 does offer a few new features. For instance, use monkey patching to add new or replace features has global effects and it might cause conflicts and bugs. In order to avoid situations like this, Ruby 2.0 limits the scope of monkey patching via Refinements. As bigger teams and bigger projects are adopting Ruby, scoped monkey patching will increasingly show its importance. There are many other features in Ruby 2.0 that are designed to cater for bigger teams and projects.

Zhou: As far as I know most of the mainstream programming languages are from America and Europe - though there are Lua from Brazil and Ruby from Japan. You too mentioned this in your book and you said this feels “lonely”. So what is the cause of this, and what can we do about it?

Matz: Well, for Lua you can include it in Europe/America too because Brazil is part of South America (chuckles). In the south eastern Asia region though there is only Ruby, and it is lonely. Europe and America still remain the most powerful regions as far as programming languages go. Asia, although has massive population, does not compete in this regard, that indeed feels lonely.

I am not sure about other countries, but at least in Japan there are many people working on programming languages, unfortunately other than Ruby none of them are well known. If more people are interested in programming and designing programming languages, there bound to be one or two that’ll break out, right? There is another hurdle in Japan - language. Most Japanese people only speak Japanese and they cannot speak English well. Funnily enough there are programming languages written entirely in Japanese. ( Zhou: In China there also are programming languages written entirely in Chinese.) In China too? I knew it! No matter how interesting these programming languages are, they will never influence anyone beyond the ones in their own country.

On a side note, I once received an email from an American. He said that you are Japanese, but Ruby looks like English because it’s written in English, why isn’t there any Japanese-written languages? I replied saying that there are, you just don’t know them, and even if you did, you wouldn’t be able to use them.

In Japan, more and more people are interested in programming, maybe because both online and in my books I always talk about how fun programming can be. Many people are now taking on the challenge of designing new programming languages. Out of these new languages, even only 0.1% of them ever get any success, I think it’s a win. I don’t know how many people want to take on the same challenge in China, Korea and other countries in Asia, but if people could look beyond programming languages are created for us, we just passively accept them, and think to create a new programming language can also be fun, then I am sure some of them will succeed.

Talking about open source projects, not many of them are from Japan, China and Korea, and I think this could be an entry point for many. There are many reasons why this is the case though, for example English is hard to learn… ( Zhou: And GitHub is also difficult to use?) Haha, is GitHub usable in China? ( Zhou: It is, it is…) Oh, that’s not too bad then. But, China’s Great Firewall still has a huge impact, many resources can’t be accessed here, right? ( Zhou: That’s right, for instance the Go programming language’s website is blocked.) Ah really? Is it because it’s made by Google? (Chuckles.) In any case, I think there are still many difficulties to face. Also, in Japan many programmers still spend most of the time at work (to put food on the table), it’s very difficult for them to contribute to open source projects. Ten years ago nobody cares about open source in Japan, but nowadays people start to realise the importance of open source, and the number of open source projects is growing. I believe China will soon follow this pattern as well, I am looking forward to it.

In the beginning no one knows what will succeed. When I started with Ruby I could not possibly have predicted its success. So I think for a programming language, timing is really important - and you’ll never know until you tried. I think in China there might also be languages that emerge from the right time that will eventually be a global success.

Zhou: In the book when you were talking about Dart, you mentioned that the ecological environment is really important for a programming language. So what do we need to do to ensure a good ecological environment?

Matz: From a user’s perspective, the most important thing is what benefits a programming language can offer. Before Rails came along, many Ruby users including myself, believe Ruby is a user-friendly language, and that’s the reason we like to use it. After Rails was born, more and more people like to use Ruby because making websites using Rails is really productive. So I think if you could communicate to your users what the benefits are by using your new programming language, it will help with its adoption and increase the chance of success.

Zhou: Thank you very much Matz! Do you have anything else to say to Chinese programmers?

Matz: In the book The World of Code I have mentioned that programming will likely to develop and evolve in the form of open source. New generations of programming languages and software are likely to emerge as open source projects. People are happy to freely use the software developed by others, and to take from that, and work on your own open source projects and make a little impact on the world - then you would become a world class software engineer. The software engineers I met in China are all very hard working and enjoying learning. I hope more of these people will step up and take on the challenge, and become world class programmers who will make a decent impact.

]]>

"Become A Better Developer You Can" - Slides of My RubyConf China 2012 Talk

2012年11月25日 13:43
Contrary to what the title of the talk suggests, I am by no means a superstar developer myself. Though I would like to think that I am extremely passionate about what I do, therefore I love to share my thoughts and tips on web development and software engineering.

Below is the version I used for my actual talk:

https://speakerdeck.com/fredwu/2012-become-a-better-developer-you-can

Since I had been preparing for my talk for over a month, I’ve done many revisions to my slides. Here’s an uncut version that contains every single slide since day one:

https://speakerdeck.com/fredwu/2012-uncut-become-a-better-developer-you-can

Hope you will enjoy them! :)

Update: The video of my talk is up! :)

]]>
❌
❌