I think my Japanese has reached a plateau, but it’s something I’m comfortable with for now. The biggest gap is in my vocabulary - I’m still looking up words in a dictionary every page I read a book or a more mature manga.
But whenever I play video games, I do it in Japanese! That’s a pretty good benchmark for me, and helps me keep practicing my Japanese aside from my weekly 30 minutes lessons.
My Farsi has not developed significantly since last year. I’m just on a slow, steady trajectory of reading one page at a time.
I did pick up one of the stories I read last year and tried it again, and I was able to get through most of it by myself! In addition, I am able to have some conversation with my in-laws. So despite the relatively slow progress I’m making sufficient progress to be satisfied.
Next year though, I’d like to invest more in my Farsi - get to the point where I can read a good chunk of literature without help from my teacher.
I focused heavily on fitness this year, similar to the tail end of last year.
I have yet to do my Dexa scan for end of 2023, but I sit at roughly 180lbs. My Dexa scan in April showed I had lost 12 pounds of body fat and gained 6 pounds of muscle since October 2022 - not bad at all for six months!
My second half of the year wasn’t as aggressive - I lost 2 pounds, hopefully fat but January we will see.
It’s been grueling to get there - my diet now consists of one proper meal a day (dinner), with protein and huel shakes in between to provide 80 grams of protein. Between that and soy lattes, I get at minimum 90 grams of protein before dinner or snacks. That’s putting me at roughly 1.2+ g protein / kg of body weight, which is less than what most recommend for building muscle mass, but I haven’t seen much of an issue.
Next year my goal is to stick my diet a bit more, and achieve 12% body fat or less. Once that happens I think I’ll try to maintain my weight and start focusing on other things.
I’ve been struggling to get more Zone 2 exercise into my routine - basically 30+ minutes of my heart rate at 70%-80% of my max heart rate.
This year I found something that works for me - playing videogames on the treadmill!
I’ve been playing an RPG that keeps me engaged - Persona 4 has worked really well for me. It also helps that it’s a long RPG, with 100+ hours to beat it.
With that I’ve been able to clear 30 minutes at a 12% incline and 4 miles per hour without issue, although I don’t get past 45 very often (too late at night or just get tired of it).
Since I’ve left Google as TL of API design, I’ve been working on a fork called https://aep.dev/. It’s been an interesting ride, discussing API design with API leads at Roblox, IBM, Netflix, and Microsoft.
I built a prototype protobuf generator as part of my work, which was a fun coding side project. Hopefully in 2024 we’ll see more established and some parts of the spec entering production use.
Similar to 2023, my 2024 goals are conservative - I’d like to focus on my family and that means all of my afternoons and a good chunk of my evenings are working on things relative to them.
But among my goals, they are:
#!/usr/bin/env python
# git-r prints the last local branches that were checked out in git.
# This is useful if you want to find the last branch that you were working on.
import subprocess
import sys
seen = set()
lines = []
output = subprocess.check_output("git log -g --oneline --decorate-refs='refs/heads' --pretty=format:'%d: %s' -100", shell=True)
for l in output.split("\n"):
if not l.startswith(" ("):
continue
if l in seen:
continue
seen.add(l)
lines.append(l)
print("\n".join(lines))
It looks like configuring alternate buttons to scroll on a regular mouse is really hard to do on Linux, but I’ve found that trackballs support the ability to click to make the trackball a scroll wheel, like the EX-G PRO Trackball from Elecom.
Click to scroll isn’t turned on by default, but it’s easy enough to turn it on in Linux:
xinput set-prop "pointer:ELECOM TrackBall Mouse EX-G Pro TrackBall" 'libinput Scroll Method Enabled' 0 0 1
xinput set-prop "pointer:ELECOM TrackBall Mouse EX-G Pro TrackBall" 'libinput Button Scrolling Button' 11
But this doesn’t persist when you unplug and plug the device in!
I tried to automate this via udev rules, but unfortunately I never got it to work (dmesg gives me no output at all).
Instead, an xorg config rule and restarting x did the trick:
Section "InputClass"
Identifier "Elecom TrackBall"
MatchVendor "ELECOM"
MatchProduct "EX-G Pro"
Driver "libinput"
Option "ScrollMethod" "button"
Option "ScrollButton" "11"
EndSection
For this post, I’m using “platform” to refer to more than one service that together provides functionality or value to a customer. Think clouds where compute, disks, blob storage, and databases are provided and interoperate with each other. Or GitHub which provides repositories, issue tracking, project management tooling, and continuous integration.
These services are designed to interoperate with each other, and often come with a set of shared clients that consume them. Think command lines, visual user interfaces, and SDKs in various languages.
Clients start out very simple: there are usually a small handful of services that exist, and the integration occurs manually. Not a lot of thought is given into what is sharable or not at this phase, sans common industry wide standards like HTTP or authentication. The services have autonomy, and the integration cost with clients seems marginal to an organization since the feature set is small.
However, as time goes on, this cost of client-server integration becomes costly. What’s worse, if there’s a desire to introduce some common feature in a client for all services, it is difficult to do so since every client is hand-coded. For example, if generic functionality around listing resources is desired, it must be added to every integration, and must be integrated by hand as likely the service’s implementation of listing resources differs.
Since everything in the clients is hand-written, there is little value in developing standard behavior, so services continue to implement differing functionality that serves a similar purpose.
This whole loop is an example of what I’m calling a “complexity cycle”:
So how do we address this cycle of complexity, that will erode the value of a common platform? By promoting (or sometimes enforcing) uniformity.
If non-uniform schema or behavior makes it harder for clients to integrate with services, then more uniform schema behavior reduces the cost. If the client depends only on uniform behavior, then the clients are extremely simple to maintain and author: their cost is effectively O(1) to integrate with all compliance services.
This in turn motivates the services themselves to be more consistent: new services, as they are created, must adhere to the uniformity requirements (even if implicit) as the clients do not allow configuration or hand-written code.
This opposite a complexity cycle can be described as a “uniformity cycle”:
A uniformity cycle can keep costs low across both services and clients:
Reversing the cycle from complexity to uniformity requires a strategy that spans across both the platform and it’s clients. Oftentimes a single individual does not have the ability to flip things, nor does a single change flip the cycle.
But several patterns exist, including:
After time, the cycle can flip.
No. It is a common case for the need to diverge in behavior to be justified, and a necessity for a platform to become more configurable to address it.
However, it is best to minimize the variation for the services and the amount of flexible handling in the client.
]]>The solution I’ve come to is that the fourth sage has the label WWW, with the contents of his box WWB. My reasoning is as follows:
The puzzle states:
The first sage takes out two black balls and says, “I know the color of the third ball.”
If the sage has taken out BB, there are only two options for the final ball: W and B. The only way for the first sage to know the answer is if the sage has a label with the opposite. For example, if the label is BBW, then the contents of the box must be BBB since we know the contents of the boxes must not match the label.
Therefore, there are two options for the first sage:
Sage | Label | Ball |
---|---|---|
1 | BBB | BBW |
1 | BBW | BBB |
Onto the second sage, who says:
The second sage takes out one black and one white ball and says, “I know the color of the third ball.”
We know the contents of the box must be BBW or BWW. Similar to the first sage, we can assume the second sage has the opposite pairing:
Sage | Label | Ball |
---|---|---|
2 | BBW | BWW |
2 | BWW | BBW |
The third sage is interesting:
The third sage takes out two white balls and says, “I don’t know the color of the third ball.”
The third sage has the following information at this point:
So what label could sage 3 have such that he cannot eliminate either BWW or WWW as a possible candidate?
Among the labels BBB, BBW, BWW, and WWW, the valid candidate are BBW and BBB:
So the third sage must have the labels BBB or BBW, the opposite of whatever sage 1 has.
So sage 3’s options are:
Sage | Label | Ball |
---|---|---|
3 | BBB | WWB |
3 | BBB | WWW |
3 | BBW | WWB |
3 | BBW | WWW |
So now we get to sage 4:
The fourth sage says, without taking out any balls, “I know the color of all the balls in my box and also the content of all the other boxes.”
Sage 4 has the same knowledge and reasoning as we do. So we know that:
Since sage 1 and 3 have labels of BBW and BBB, then sage 2’s label must be BWW. Since all other labels are taken, sage 4’s label must be WWW.
If sage 2’s label is BWW and they know the contents of their box, then their box must contain BBW.
This also means that sage 1’s box is clear: it is BBB, which implies that sage 1’s label is BBW:
So we know the following:
Sage | Label | Ball |
---|---|---|
1 | BBW | BBB |
2 | BWW | BBW |
3 | BBB | WW* |
4 | WWW | ??? |
Since the sage knows their label is WWW, they know that their box must not contain WWW. Since sage 3 has drawn WW*, it must contain WWW. By deduction the last sage must have the oly remaining combination, which is WWB:
Sage | Label | Ball |
---|---|---|
1 | BBW | BBB |
2 | BWW | BBW |
3 | BBB | WWW |
4 | WWW | WWB |
I wanted to dive in a little bit more, here are some findings and thoughts.
note: I am not a medical professional, and nothing I say here constitutes any medical advice. It’s my own opinions and theories.
To answer the most important question first: I think there’s evidence that at minimum, sucralose does not lead to a significant increase in cancer. Several studies exist that show at best a minor increase (~10%) correlated with sucralose.
That said, the following concerns is making me consider removing sucralose from my daily diet:
The NAFLD one especially gives me pause, as it is a good marker for metabolic health.
Over the past year (2023), I’ve lowered my sucralose intake by 75%, down to roughly one sweetened protein shake a day.
It’s worth noting that the study talks about in-vitro experiments: these are done in a petri dish or something similar, and not in a live animal. Therefore there could be other biological processes that make this a non-concern. On the other hand, there could also be processes in an in-vivo environment that make this worse.
At it’s core, a genotoxin is something that is known to break down DNA. Since cancer is caused by mutations in the DNA, genotoxins are highly correlate to cancer.
I found a paper that explains the definition in a little bit more detail, at least defining what genotixicity means in the context of carcinogens. The summary is genotoxic carcinogens directly affect DNA and that is the cause of the carcinogen, while non-genotoxic uses some other mechanism, perhaps indirect like hormonal.
It’s not super clear if there is such a thing as non-carcinogenic genotoxin. The abstract states that there is no such thing as a same threshold with a genotoxin: It’s really just a matter of risk level.
Assuming the study holds true, what amount of sucralose is safe?
The amount of sucralose relative to a microgram of sucralose-6-acetate is ~150. so 150μg of sucralose per 1μg of sucralose-6-acetate.
So for an 80kb person, the daily limit would be: 80 * 0.0025 = 0.2μg. So * 150 that would be at most 30μg of sucralose-6-acetate.
The US sucralose recommended daily allotment is 5mg/kg bw /d. most drinks contains at least 10mg.
So 10mg of sucralose would be 333 times the amount of genotoxin I should be ingesting.
Effectively, there is no practical amount of sucralose that is safe.
The study is worth taking seriously: clearly sucralose contains a lot of a genotoxin. However, this doesn’t correlate well with the fact that multiple studies have shown that sucralose does not lead to a meaningful increase in cancer.
Although I won’t be buying any more sucralose products, I think the above means one of three things:
This is some notes about how I eat, and why.
People who eat a plant-based diet live 7 years longer.
It’s hard for me to go completely vegan (I like my seafood and dairy), so I try to go for vegan meals most of the week, with 1-2 dinners non-vegan.
From what I read in Blue Zones (above), eating meat once a week still helps you get a lot of the longevity benefits.
Trying to eat something fairly health, I’ve found that oatmeal is more filling than other vegan breakfast meals I’ve tried:
I add almonds and some high-in-fiber fruit like banana to stay sated.
Huel aims to provide sustainable, plant-based meals cheap. Their normal offering is a meal replacement shake, which I can’t get myself to consume regularly. I like their “Hot and Savory” meals because:
“Hot and Savory” is amazing for that: I’ve fiddled with various plant-based lunch combinations and found it impossible to find something filling.
To help get a boost of protein, I drink a shake made from protein powder (plant-based) and almondmilk.
For dinner I’m pretty loose, since the rest of my meals are quite regimented. A majority are vegan or minimal animal-based (grain bowls, veggie burgers and fries, phad thai), and sometimes things like sushi, grilled salmon, past, etc.
]]>2022 was a largely unventful year for me professionally: my kids are going to new schools and kindergartens, so a lot of my focus was on my family, more or less as planned.
This was also supposed to be a year of finishing up loose ends: I think I have continued to make some progress there.
I finally shipped a version of tome that I was happy with! This was a side project of mine that was mostly there, I just needed to take it over the finish line.
I’m happy to have done so! Tome helps me in my daily tasks, and maybe it’ll help you too.
Professional needs aligned with my love for Open Source, and I ended up working on the cloud.google Ansible provider, bringing it back up to snuff as an Ansible collection certified for ansible-core 2.13 and above.
I’m pretty proud of my work here, totalling 50 commits in 2022Q4:
Culminating in the release of 1.1.2, the first release in almost a year and a half.
Unfortunately my time allotments don’t let me spend that much time on the project, but my intention is to keep it healthy and accept PRs with the new contribution process documented.
I continue to work on my Japanese, but I think I’ve hit a hard plateau there: I have hundreds of words I still need to learn, and I’m not really remembering any of them with my flash card system.
For next steps, I think I’m going to just continue to grow my Japanese with continued weekly italki classes for speech and reading to try to get to the point where there’s almost no words I don’t know.
I think consuming more TV / Films could be helpful: I don’t get enough exposure from a spoken perspective, to pick up on common conversationalexchanges
My Farsi has been getting better as well: it’s been roughly a year and half since I started to learn.
I can’t say I’ve dedicated a lot of time here, but I’m able to:
I think with the limited time I have, I won’t make much progress in 2023 either. But as my biggest stumbling block is in vocabularly, I hope to spend a lot of time fleshing that out so I can muddle through conversations.
I made some minor, but significant strides this year health-wise:
I lost 4 pounds purposefully, from 188 to 184, in 2022Q4. It’s a major step forward for me since I haven’t sucessfully been able to lose weight on purpose before. Although I was down to 190 from 200 right after the pandemic started (presumably due to me eating salads for lunch daily during that time), it wasn’t a purposeful action.
I was able to run a 5k in a single run, and actually even started running 5k’s regularly throughout the week! 18 year old me never even completed the mile run, so to get to a 5k 16 years later is quite an improvement for me.
My focus on a plant-based diet is the same as 2021: I generally eat plant-based meals for most meals in a day. Near the tail end of the year I’ve been focusing on a high-protein, calorie restricted diet with the goal of getting to 15% body fat to start (I was 21% according to a Dexa scan in October).
My diet looks on a regular weekday looks like:
My goal is to get to 1.5g / protein / KG bodyweight, and also restricting to 1700 calories a day. Generally some sweet or snack gets in the way, but that’s my target.
2023 will be another year of conservative goals for me: there’s a lot of family obligations and some random personal dablings I’d like to spend my time with.
That said, there’s some goals I want to continue progress on. Here’s a list:
2022 was again a non-transformative year for me, but making steady, principled progress on my goals has been great.
Happy New Year!
]]>I regularly use wake-on-lan on my local network to start up my gaming desktop, but I began to wonder if it’s possible to wake my machine up from the public internet (The “WAN”)? And here is how I got to successfully accomplish a wake-on-wan.
My existing setup already enables me to stream Moonlight over wan. I accomplished that by:
Wake on lan works by sending an ethernet frame with the contents of the MAC address. This is often send to the broadcast IP of a network, ensuring that all devices connected to the network will receive it.
I originally tried to do direct port forwarding of the UDP 7 through 9 ports, but I found out that my internet provider (Comcast) sometimes does not allow their modem to forward the packet.
As such, I ended up doing a separate port forward from an arbitrary port and mapped it to port 7. And that was it!
If you have issues, try the following:
255.255.255.255
) the request directly.Here is a quick demo of creating some scripts in a directory, and a command s
from it:
You can find the documentation here, and the source code and releases on GitHub.
Give it a try and please file bugs or give feedback!
That’s really what this post is all about, but if you want to hear more about why and the history, read on.
In 2011, I worked at Zillow, as a relatively new engineer obsessed with efficiency. I loved writing little scripts that helped simplify my workflow, such as:
At the time, there were a lot of small things that needed to be done to start developing.
Eventually someone posted about sub in our engineering mailing list, which was created by 37 Signals, a company also obsessed with the developer experience and had a bit of a following at Zillow.
Sub was a great tool: it provided the ability to bootstrap a single command from a directory of scripts, and I saw an opportunity to use this to create a community-owned pool of scripts to help Zillow developers be productive and share productivity tooling as well. I created a git repository, called it zb, and shared it with the other engineers. Zillow-bootstrap did some other things too (like local environment setup with some Python scripts), but the zb
commands were the most famous.
zb was used for years at Zillow after that. We even forked the code and added some features like shell script sourcing, which enables setting environment variables in the parent executing the command.
On top of usage at an organization, I found sub to be very useful for my own scripts as well. Single-letter directories help me quickly navigate to the commands that are the most useful, and provide some namespacing.
I call this s
in my setup, short for sub.
sub itself was fairly stable for a while, but it had some challenges.
Sub’s design expects the user to fork the whole repository to generate your own command. This coupling of the user’s scripts with the scripts of the core sub command made it difficult to stay up to date, due to dealing with merge conflicts and mapping variables that were renamed to initialize the script.
sub was written in bash, and in particular relies on commands that are generated to build functionality like completion, etc. It makes the code hard to reason about.
In addition, bash is not the most performant language in the world, so more complex commands can become slower. Here’s the amount of time it takes to call sub
, which maps to help
:
$ time sub
real 0m0.122s
user 0m0.081s
sys 0m0.053s
Considering I may invoke sub hundreds of times in my day, even gaining 50 milliseconds per command would save me a few minutes a day. I think in practice this doesn’t mean a lot, but to the performance-obsessed, any opportunity for optimization looks promising.
One of the best parts of sub is its simplicity and minimal set of functionality, which made it an appealing project for a re-write! After a couple of months, Tome was born.
Tome addresses the challenges above :
tome
).The initialization is straightforward as well: add tome
to your path after downloading it from the releases, and initialize with a one-liner in your rc file:
eval "$(tome init my-commands ~/my-scripts zsh)"
where
my-commands
is the command you want to make (I recommend a short, 1-2 character acronym to not type a lot).~/my-scripts
is the directory with the scripts and directories.zsh
should be replaced with the shell you are invoking.In particular I’m very happy with the choice of Rust: it enables fairly portable static binaries, as well as a significant boost in performance:
Example of calling help using tome (rust):
$ time s
real 0m0.003s
user 0m0.003s
sys 0m0.000s
Recall the 100ms+ time when using sub above.
One of the last things I did before I left Zillow was switch us over to use Tome, although in the 2 years since then It could very well be that Zillow-bootstrap use has decreased significantly: the Zillow tech stack was rapidly modernizing, and then eliminated the need for many of the scripts that were used to bootstrap the dev environment.
That’s it! Please give Tome a try.
]]>