While commendable, your concern regarding adding and dropping columns is mostly misplaced.

Adding a nullable column is a metadata-oinly operation: that is, it only entails minor changes to the database's definition of the table in internal tables, and does not require rewriting the clustered and all the non-clustered indexes. This means that such a modification is very fast. Likewise, dropping a column is also metadata-only, as the storage engine will simply ignore that data until each page is eventually overwritten without it.

You can see this in action in [this fiddle][1]. `SET STATISTICS IO ON` is used to show reads of the table itself, and you can see that it only happens on the addition of a non-nullable column.

____
Your only real concerns should be:
* Possible blocking chains.  
For example, a long-running `SELECT` holds a `Sch-S` lock on the table. The `ALTER` tries to take a `Sch-M` lock and waits. All other `SELECT` and modification queries then pile up behind it, waiting on their own `Sch-S` lock which they can' take.  
Unfortunately, this can't be avoided using `WAIT_AT_LOW_PRIORITY` as that's not yet implemented for this type of `ALTER`. Your best bet is to put the following before your `ALTER`:  
    ```
    SET LOCK_TIMEOUT 10000;  -- milliseconds
    ```
* All views and procedures which access this table will need their metadata refreshed. You can use the following script, which gets 
all such objects and refreshes them:
```
DECLARE @sql nvarchar(max) = (
    SELECT STRING_AGG(N'
EXEC sp_refreshsqlmodule N''' + QUOTENAME(s.name) + '.' + QUOTENAME(o.name) + ''';',
      '')
    FROM sys.objects o
    JOIN sys.schemas s ON s.schema_id = o.schema_id
    WHERE o.object_id IN (
        SELECT ed.referencing_id
        FROM sys.sql_expression_dependencies ed
        WHERE ed.referenced_id = OBJECT_ID(@yourTable)
    )
      AND o.type IN ('P', 'V', 'FN', 'IF', 'TF', 'TR')
);

PRINT @sql;

EXEC sp_executesql @sql;
```


  [1]: https://dbfiddle.uk/fesKVKOw