Habits, Systems, and Scripts

I’ve been ‘auditing’ various areas of my life.

After being obsessively focused on work for a long time, it’s been helpful to think deeply about the habits and pre-written scripts that I’ve developed across a bunch of areas of life. In most cases, the habits I’ve adopted over the years are a function of taking the path of least resistance than an intentional choice.

Here are some things I’m looking at:

Information Consumption (news, email, podcasts, reading, etc) Exercise Eating Prayer Marriage Friendship

Thinking through what I want from these categories, where I’m not satisfied, and asking ‘why’ multiple times until I get the root cause of the delta has been insightful. It’s allowed me to rewrite the default scripts I operate out of and make meaningful change very quickly.

It takes intentional effort & dedicated time to create clarity in your thinking to focus in on what needs to change and to generate the excitement to do it.

Take some time and audit areas of your life that you’ve ignored because of laziness or busyness. It’s worth it.

Continue Reading

Handling Web Timeouts in Heroku

I’m a huge fan of Heroku. Way back when, I used to manage the entire deployment infrastructure manually. I’d grab a VPS from RackSpace/AWS, install nginx, configure ruby, tinker with deployment scripts, and then in the weeks ahead endlessly tinker with settings when things didn’t work just right. Although I did enjoy the capture-the-flag feel of finding the right service configuration to solve a problem, once Heroku became a thing I switched over every application I managed.

There’s a huge amount of leverage in never having to worry about the details of your deployment infrastructure. Heroku is expensive, but it’s orders-of-magnitude cheaper than hiring a devops expert.

However, there are some limitations. The one you’ll most likely run into is the 30-second web worker timeout. If your web request doesn’t finish in time, it will be killed and the user receives a 500 error. Not good.

A much better UX is displaying some sort of ‘loading slowly, please refresh’ message to the user and implementing progressive caching. This way, if there is some sort of slow service causing an IO block, you can cache the response in the first request made by the user, so the second page load attempt works successfully.

(You may be wondering why a page load would ever take 30s. Great question. I work a lot with NetSuite, and sometimes need to pull content dynamically. If there is an API slowdown—which happens often—this can cause the page load time to spike).

The best way I’ve found to gracefully handle this situation is to use the ruby stdlib Timeout::timeoutmethod to throw an exception after 29 seconds. However, this method is dangerous. You’ll want to first understand how this operates under the hood:


In your rails controller, here’s how you can ‘protect’ a method that could run for a long time and display a friendly timeout page instead of a standard 500.

class ApplicationController rescue_from WebWorkerTimeoutError do render :timeout end around_action :raise_on_web_timeout, only: :show def show @state = the_long_running_thing end def raise_on_web_timeout Timeout::timeout(29, WebWorkerTimeoutError) do yield end end end

Continue Reading

The Feeling of Fast

Momentum is a powerful force. Once you are moving it’s easier to get keep moving. If you’ve already crossed off an item or two on a less-than-ten-item todo list, you’ll be able to get the rest of the tasks done faster.

The perception of speed is sometimes more important than your absolute speed. I’ve found that the “feeling of fast” creates the momentum that drives effective execution.

It’s worth spending the time and money to optimize various aspects of your work & life in order to create a feeling of moving quickly.

For example:

A faster computer or phone reduces friction and makes it feel like you are getting more done. Using more keyboard shortcuts, or writing tools to automate repetitive tasks, can increase your velocity. Getting a treadmill desk can literally create the feeling that you are moving quickly. Using a VA to offload tasks which are time-consuming, boring, and uninteresting can make it seem like you got them off your plate quickly. If you have a tendency, as I do, to analyze small decisions too much making a decision within a pre-determined time-box can keep you moving quickly. If you are constantly refilling your Brita water filter, buy an under-the-sink filter with a reservoir so you’ll never need to wait for water to filter.

Think about what is annoying, what makes you feel slow, and improve it. It’s worth the up-front investment.

Continue Reading

2018 Goal Retrospective

Every year I enjoy doing a retrospective on last year’s goals. I believe there is value is “open sourcing your brain”: your ideas may help someone and you may get invaluable feedback to help you improve.  There’s also a strange sense of social accountability created for myself that helps me ensure I improve this year.

So, with that in mind, here we go!

What worked Doing something small to move a goal forward. Once you see progress you’ll be motivated to dig into the goal more and make progress. Reduce friction on goals which you are not naturally motivated to achieve. For instance, if you want to drink more water everyday, buy a water purifier to make the water taste better. Scheduling time on Sunday to work on goals. Other days of the week are very busy and my mind is generally “spent” by the end of the week.

Some additional context on #1 & #2:

One of my goals this year was improving my health: losing weight, eating better, etc. This is something I’ve never been good at or motivated to improve.

I’ve found that if I’m not making progress on a goal, especially when I’m not naturally motivated to hit it, it’s because there is not a clear path to making progress. For instance, my health goal was to hit a specific weight and BMI. I don’t know how to work out: my family, friends, and school never taught me and I never invested in it. I have such little knowledge of the space I don’t really know where to get started.

What was really effective for me was setting up a little space in my house where I could do pushups and planks. I’m not sure if those exercises were the “best” to do, but I know how to do them well enough. I can do them at home, they don’t take much time, and it’s easy to see progress. I walk past the area I setup and it requires so little mental energy to take 10m and do the exercises that I’ve ended up doing them more consistently.

The lesson here for me is to break down large goals into a small actionable task and reduce the friction involved in accomplishing the task.

What didn’t Quarterly review. We (my wife and I) did a great job on our weekly and monthly reviews, but just didn’t do the quarterly reviews. We also didn’t acknowledge this and adjust through the year. Health goals. I don’t enjoy working out. It has always been a chore that required a bunch of my daily “decision making quota” to push myself to do it. I started making progress at the end of the year, but overall I barely made progress. Not scheduling enough time on Sunday to work on goals. There was one goal this year which I didn’t hit just because I didn’t schedule a couple more hours to create a plan and strategy that I could easily execute on throughout the week. Not bringing in help/accountability on a goal that is slipping. I’m wired to figure things out on my own, but sometimes (ex: exercise) it’s best to bring in a professional to help provide external motivation and personalized information to put me on a clear track to accomplishing my goal. Developing new habits. I only have so much decision making power, or discipline, each day. Each time I need to make a decision I withdraw from my “decision bank account”. This is why automating or eliminating as many decisions as possible is very powerful. It’s also why I can’t seem to develop more than one or two habits at a time. This last year I was not intentional about which habits I was working on and didn’t ensure I had enough “discipline budget” to effectively build the habit. Running a business by yourself can take over your life. It’s very hard to do anything but work on important and urgent items for the business. I created a “2018 Goal Dashboard” and didn’t look at it at all. Keeping yet one additional thing up to date wasn’t effective and it wasn’t inspiring keeping a digital sheet full of gauges up to date. The meta-lesson here is being aware of adding more weight to a process. Be thoughtful about what the minimum overhead is required to accomplish your goals. What am I going to change? We didn’t stick to our quarterly reviews. I think we need to shorten the review to make it quick: ~30m. The length of time that it takes to effectively do the review is what caused it to be harder to execute on. Don’t attempt to make big life or habit changes when you are starting a business. Be honest with yourself and realize that creating a successful business is going to take every ounce of effort you have and every other major category of your life will be on autopilot. The type of business does matter here: if you are creating a coaching business, a small info-product business, etc that you aren’t relying on to provide for your family this is a different story. Every 2-3 months we need to schedule a 3-4 hour block of time on Sunday to work on our goals. This dedicated time on a day when there’s no other urgent obligations is critical to creating a strategy for moving forward on the more challenging goals. There’s got to be an easy physical way to update the large goals for the year. Maybe some sort of board with the goals written down that you visit throughout the year.

I’ll report back in 2020 with how this year’s goals went.

Continue Reading

How to Create a Custom Field to a NetSuite Customer or Transaction

NetSuite custom fields are incredibly powerful and flexible, but they are not easy to work with! Below is a quick guide that walks through how to create customer and transaction custom fields and some tips and tricks for working with custom fields.

How to create a custom customer (i.e. entity) field in NetSuite: Navigate to: Customization > Lists, Records & Fields > Entity Fields > New Label: use a name you’d like to see displayed on your customer. Type: “Free-form Text”. There are cases where you won’t want to use free-form text, but in most cases this is the best option. ID: “_a_internal_identifier”. Note that you should start your identifier with a _ since custentity will be prepended to whatever you provide here. You’ll want to change “_a_internal_identifier” to an ID that is descriptive of this field in your setup. Store value: checked Applies to: Customer Save How to create a transaction (invoice, cash sale, order, payment, etc) field in NetSuite: Navigate to: Customization > Transaction Body Fields > New Label: use a name you’d like to see displayed on your transaction. ID: _a_internal_identifier. Note that you should start your identifier with a _ since custbody will be prepended to whatever you provide here. Type: free form text Store Value: checked Applies to: Sale, Payment, etc. Where you want this field displayed is up to your use-case. Global Search: checked. This enables you to search for the value in the NetSuite global search Save Other Tips & Tricks for Working With NetSuite Custom Fields Customer (entity) custom fields will always start with custentity. Transaction (invoice, sales order, payment, etc) custom fields will always start with custbody. You can create custom fields on most, but not all records. For instance, you can’t create a custom field on the currency record in NetSuite but you can create one on a subsidiary. It’s possible to access “hidden” fields that are not available through SuiteTalk or SuiteScript using field defaults or formula field defaults on custom fields. You can source values from a related record. For instance, if you want to source a value from a CashSale connected to a CashRefund you could use a formula like {createdfrom.externalid} as the default value for a field that doesn’t store a value. A field which does not store value (i.e. a field that just contains a default value) is not available via a formula on another field. A field not found error will be reported. In other words, you can’t nest formulas. This is a huge bummer. If “formula” is not checked when creating the custom field you can still substitute field values using the {field} syntax You’ll get ERROR: Invalid Expression for lots of different types of errors. For instance, if your formula doesn’t output the correct field type you’ll get this error. This makes it very challenging to debug why a formula isn’t working. Join fields (i.e. {entity.datecreated}) strips the value of its type. Everything comes over as a string. You can use SQL functions in a formula field default

Continue Reading

2017 Goal Retrospective

I love yearly goal planning with my wife. It’s been an awesome practice that we started years ago thanks to The Best Year Ever program and the promptings of David DeWolf. It’s truly changed our lives: it’s amazing to see how many big, important goals you can hit each year if you have focus and clarity at the outset of the year.

I try to do a retrospective in most areas of my life, but I especially enjoy doing one in regards to my yearly goals. I’ve found identifying and implementing small, incremental improvements to important aspects of life to be incredibly useful.

What worked:

Our weekly review reminded me of key goals and maintained momentum towards hitting them, even when I lost motivation or felt overwhelmed with other demands on my time. This last year was a busy one for us, and we paired down our goals from the outset to account for this. Being realistic about what can be accomplished based on your season of life is important. Although our goals were “boring” this past year, they were the right ones for our season of life. It’s ok to have boring goals that are important—but not every year! Over the long-term, they need to be exciting. Two of my goals were habits that I wanted to develop. I think if there were more than two habits, this wouldn’t be as effective, but having two habits to focus on each week was very powerful.

What didn’t:

We only did our quarterly review once. We use the quarterly review time to edit our goals (and sometimes remove!), change our strategy, or adjust goal metrics if what we originally set out to do was unrealistic. Additionally, not having this time set aside eliminated the possibility for us to have dedicated time to work on key goals that were slipping.

I also find that keeping goals around that need to removed or edited is demotivating. I skip over them and immediately discount them, which affects how I perceive the rest of the goals on my list.

Here’s what needs to change:

We need to add more fun to the quarterly review process. For the yearly review, we try to take a trip and get out of our normal life for a couple of days, but for the quarterly reviews we’ve never done this. Combining the process with a fun experience will help us look forward to the process more and make sure it happens. A Groupon hotel deal is a great (and cheap!) way to make this happen. There’s a distinction between key habits and goals. For us, we’ve always treated habits that we want to develop as goals. However, there are important habits that we’ve developed (or are still working on!) that we want to make sure we maintain. I’m going to try to develop a list of habits and creatively think of a way to track our commitment to critical habits over time. Not sure how to do this yet without creating more work/time for us.

Continue Reading

Notes on Working with Jobs (Projects) using the SuiteTalk NetSuite API

Jobs are a confusing part of the NetSuite system. Here’s some notes and resources to help make handling jobs easier:

Jobs are named Projects in the GUI There are two versions of jobs: “standard” and “Advanced Jobs”. They both use the same core job record but change how the data is structured on the customer. Jobs have a kcustomer attribute that is not accessible via SuiteTalk which represents the customer that the job is associated with. This field is accessible via SuiteScript. You could most likely expose this field by creating a formula field on the job record with {kcustomer} as the formula. There is no customer field on the job schema However, there is a customer field on the job search columns. You can use an advanced search to return the customer associated with a job. If “Project Management” is enabled (aka ADVANCEDJOBS) the entity reference on the customer is a customer during the edit view, but will switch to be a job reference when the record is saved. When ADVANCEDJOBS is disabled the job reference could be either a customer or a job and the job field is not displayed. The parent field on a job can reference a customer. I believe you can have an heirarchy of jobs so it can be possible for the parent to be a job reference. Since the kcustomer field is not exposed via SuiteTalk the only way to determine the correct customer reference is to walk up the hierarchy.


Determine the customer associated with a job via SuiteTalk SuiteScript to extract a valid customer reference from an entity ID Determining if an entity ID is a customer or a project

Continue Reading

Notes on Dates & TimeZones with NetSuite’s SuiteTalk API

Setting date and datetime fields in NetSuite using the SuiteTalk API is tricky business. Here’s some notes on how to ensure that the date you push over to NetSuite persists the way you’d expect regardless of the server, local server, user, and company timezone.

The API is sensitive to the timezone of the current user. If you push the same datetime value to the XML via SuiteTalk, and change the timezone settings on the user preferences page, the resulting date set on the record will most likely change. The timezone is not available on the employee record. Also, the timezone is not available on the company/subsidiary record. It is impossible to determine the timezone set on the user or the company from the SuiteTalk API. The get_server_time method does not return the time in the current user’s timezone preference. There is no SuiteScript-specific method to getting a timezone. Although it looks like you can infer it from the stdlib JS responses. It could be possible to create a RESTlet bundle that could retrieve the timezone of the current user as a JSON response. If the timezone of the user (employee) differs from the timezone of the request NetSuite will convert the datetime in the request to the timezone of the current user. If you push a date of 2017-04-17T00:00:00-07:00 and the user’s timezone is GMT -04:00 the resulting date field in NetSuite returns 2017-04-16T21:00:00-07:00. This appears properly as 04/17 in the GUI for the current user as well as for other users with different timezone configurations. If you push a date in the timezone of the NetSuite servers (PDT) NetSuite seems to ignore the timezone of the user used to make the request. Note that accounting for DST offset and zeroing out the hours, minutes, seconds is essential to making this work. Here’s an example of what your date should look like 2017-04-17T00:00:00-07:00. If you push a date one second after midnight NetSuite will take into account the user’s timezone. Best practice: convert your date to UTC0 and use NetSuite::Utilities.normalize_time_to_netsuite_date(time_stamp). This utility will handle DST, zeroing out hr/minute/seconds, and convert the datetime to the right format for SuiteTalk. The date returned by the API is adjusted based on the user’s timezone. For instance, if you push 2017-04-17T00:00:00-07:00 to NetSuite with a user whose timezone is set to EST the date returned by the API will be 2017-04-16T17:00:00-07:00 although it will properly display as 2017-04-17 in the GUI.


NetSuite ruby client conversion utility https://netsuite.custhelp.com/app/answers/detail/a_id/34163/kw/TIMEZONE https://netsuite.custhelp.com/app/answers/detail/a_id/43268/kw/TIMEZONE https://netsuite.custhelp.com/app/answers/detail/a_id/44687/kw/TIMEZONE Converting between time and datetime in ruby Modifying time without railscn

Continue Reading

Fixing Bundler Issues After a macOS Sierra Upgrade

I recently upgraded my laptop, and with that came setting up a fresh dev machine. With a fresh install always comes with a set of development configuration problems. Here’s some tips of resolving ruby bundler issues in MacOS Sierra.

These tips assume you are using dotfiles that look similar to these. Also, I prefer fresh installs, so I never pull config from my old machine/OS. These fixes may not work if you are migrating configuration from a previous OS.

For any of the fixes outlined below:

Adjust the version strings based on your project If you do not have bundler’s shared gems disabled, then you can omit the --install-dir directives.

An error occurred while installing pg (0.18.4), and Bundler cannot continue.

gem install --install-dir vendor/bundle pg -v 0.18.4 -- --with-pg-config=/Applications/Postgres.app/Contents/Versions/9.6/bin/pg_config

Alternatively, you can configure this setting with bundle config build.pg.

An error occurred while installing nokogiri (1.6.8), and Bundler cannot continue. An error occurred while installing mysql2 (0.3.20), and Bundler cannot continue. xcode-select --install

An error occurred while installing capybara-webkit (1.11.1), and Bundler cannot continue.

brew install qt55 brew link --force qt55

Then, I needed to manually edit mkspecs/features/Mac/default_pre.prf. More details here.

error occurred while installing therubyracer (0.12.1), and Bundler cannot continue

gem install --install-dir vendor/bundle libv8 -- --with-system-v8 gem install --install-dir vendor/bundle therubyracer -v 0.12.1

rubygems/core_ext/kernel_require.rb:54:in `require’: cannot load such file — bundler/setup (LoadError)

This is caused by spring coupled with a new version of bundler. Regenerate your binstubs:

bundle exec spring binstub --all

Enter passphrase for /Users/USER/.ssh/id_rsa

If you copy your public + private key from your old machine/OS, and used a passphrase to secure it (which you should be doing!), you’ll need to run this command to avoid reentering your passphrase every time you use the key:

ssh-agent bash ssh-add # in some cases (capistrano deployment, for example) you'll need to do this: ssh-add -K ~/.ssh/id_rsa # Also, in some cases, this helped: edit ~/.ssh/config Host * UseKeychain yes

An error occurred while installing eventmachine (1.0.4), and Bundler cannot continue.

Specifically, I got this build error:

Gem::Ext::BuildError: ERROR: Failed to build gem native extension. ~/.rbenv/versions/2.1.6/bin/ruby extconf.rb checking for rb_trap_immediate in ruby.h,rubysig.h... no creating Makefile make "DESTDIR=" clean make "DESTDIR=" compiling binder.cpp In file included from binder.cpp:20: ./project.h:112:10: fatal error: 'openssl/ssl.h' file not found #include <openssl/ssl.h> ^ 1 error generated. make: *** [binder.o] Error 1 make failed, exit code 2

Here’s the fix (inspired by this post) that fixed the issue:

gem install --install-dir vendor/bundle eventmachine -v '1.0.4' -- --with-cppflags=-I/usr/local/opt/openssl/include

/.rbenv/versions/2.2.3/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in `require’: cannot load such file — bundler/setup (LoadError)

This error was being caused by spring when attempting to run bundle exec rails console. You can verify that this is the case by omitting spring with export DISABLE_SPRING=true and seeing if rails console runs just fine.

To fix this issue, you need to update your binstubs:

bundle exec rake rails:update:bin

Continue Reading

Notes on NetSuite’s Gift Certificate Record

The gift certificate functionality in NetSuite is much more limited than other functionality in NetSuite. It’s also the only option for implementing store credit, electronic, and physical gift cards in NetSuite. If you need to integrate with the gift certificate system in NetSuite, here are some notes to help you along the way:

Gift certificates are not created directly. They are are created by creating a “Gift Certificate Item” and using that item in a SalesOrder. Gift certificates can be created without an associated posting transaction (Sales Order) by importing a CSV. However, you cannot create a standalone gift certificate via SuiteTalk. You must create a posting transaction (Invoice/CashSale) with a gift certificate item in order to create a gift certificate via SuiteTalk. Technically, a non-posting transaction (Sales Order) will create a gift certificate record. However, the gift certificate cannot be used until it is associated with a posting transaction. The “Amount Remaining” field is nil if the gift card has been created by a non-posting transaction by has not been filled/purchased. In other words, if amount remaining is nil the user should not be able to use this gift card on an ecommerce site. Gift certificate codes can be a maximum of 9 characters. It’s important that external systems respect this limit if NetSuite is not the system-of-origin for gift card codes. The gift certificate code can be blank. It’s hard to tell exactly how this can occur because there is no system log, but I believe it occurs when gift certificates are imported. There is a custom field list advances search option for gift certificates, but strangely enough custom fields cannot be added to the gift certificate item. The system log is not available for a gift certificate item The last modified date is not shown in the UI You cannot search gift certificates by the last modified date, although it is available in the SuiteTalk response. The last modified date is not updated when a gift card is used. The best way to search for updated gift cards is using a transaction search. A gift certificate is not tied to a specific customer, although in some areas of the UI it does seem as though The gift certificate record does not contain a reference to the customer that purchased or used the gift card. You must search for transactions associated with the gift certificate, then pull a customer reference from the associated transaction. Use the transaction search’s giftCertificate field to retrieve all transactions that are connected with the gift certificate. Note that this includes the non-posting transaction that may have created the gift certificate record, along with any other transactions that have effected the gift card balance over time. You’ll need to filter out the exact transactions you are looking for.` If you need to automatically generate codes that play nicely with other systems (3PL, ecommerce, etc) the best approach is create a before save via SuiteScript to call out to an external service that coordinates gift card code generation. NetSuite does provide the option to generate gift certificate codes for you. However, if you have other systems (such as an eCommerce platform), you won’t be able to push gift certificates into NetSuite using the externally generated code if this option is enabled. If a transaction’s line item contains the giftCertCode field, then the line item is a GiftCertificateItem. If a transaction’s header-level giftCertApplied field is less than zero, then a gift certificate was used as part of the payment. If you search for transactions with giftCertificate not empty ItemFulfillments will be returned in the results, although there is no reference to a gift certificate record on that transaction. A gift card notification email will be sent to the email in the “To” field. There is no way to disable this feature. Example Gift Card Transaction Search

Here’s an example transaction search using the NetSuite ruby bindings for any transactions that use a gift card:

NetSuite::Records::SalesOrder.search( basic: [ { field: 'giftCertificate', operator: 'notEmpty' } ] )

Note that not just SalesOrders are returned. Any transaction associated with a gift certificate is returned in this search. To restrict to a specific transaction type, using a type = _salesOrder search criteria.

Enhancements Requested

I requested enhancements for the more critical gift card limitations I found through SuiteIdeas (vote on them if you can!):

116981 Ability to disable gift certificate emails 411408 Ability to search by gift certificate’s last modified date 176418 Gift certificate custom fields can’t be viewed in SuiteTalk response 159896 Cannot create custom fields on gift certificate

Continue Reading