# Any chance we could reduce the bundle size in development?

**URL:** https://clojureverse.org/t/any-chance-we-could-reduce-the-bundle-size-in-development/3690
**Category:** shadow-cljs
**Created:** [January 27, 2019, 3:30am UTC](https://clojureverse.org/t/any-chance-we-could-reduce-the-bundle-size-in-development/3690 "2019-01-27T03:30:54Z")
**Posts on this page:** 12
**Page:** 1

<div class="post-metadata">

### Author: ![jiyinyiyong](https://clojureverse.org/user_avatar/clojureverse.org/jiyinyiyong/32/85_2.png) [@jiyinyiyong](https://clojureverse.org/u/jiyinyiyong)
#### Post date: [January 27, 2019, 3:30am UTC](https://clojureverse.org/t/any-chance-we-could-reduce-the-bundle-size-in-development/3690/1 "2019-01-27T03:30:54Z")

</div>

Even though my own code only contains tens of modules, shadow-cljs has too load a bunch of files from Closure Library and Clojure standard library. Most of code can be dead code eliminated during optimization:

```auto
=>> du -a *.js | sort -n -r
4224	cljs.core.js
1152	cljs.pprint.js
640	cljs.spec.alpha.js
640	cljs.core.async.js
256	shadow.object.js
256	shadow.dom.js
256	shadow.animate.js
256	cljs.tools.reader.js
256	cljs.spec.gen.alpha.js
224	goog.dom.dom.js
192	goog.base.js
152	goog.style.style.js
120	shadow.cljs.devtools.client.browser.js
120	goog.array.array.js
104	shadow.cljs.devtools.client.hud.js
104	goog.string.string.js
88	goog.uri.uri.js
88	goog.promise.promise.js
88	goog.net.xhrio.js
88	goog.iter.iter.js
80	goog.html.safehtml.js
80	cljs.tools.reader.reader_types.js
72	goog.uri.utils.js
72	goog.events.events.js
72	cljs.tools.reader.edn.js
64	shadow.cljs.devtools.client.env.js
64	goog.math.long.js
64	goog.i18n.bidi.js
64	cljs.core.async.impl.ioc_helpers.js
56	shadow.xhr.js
56	goog.debug.logger.js
56	cljs.tools.reader.impl.errors.js
56	cljs.core.async.impl.channels.js
48	shadow.cljs.devtools.client.console.js
48	respo.render.diff.js
48	goog.object.object.js
48	goog.math.integer.js
48	cljs.reader.js
40	goog.useragent.useragent.js
40	goog.result.resultutil.js
40	goog.labs.net.xhr.js
40	goog.html.safeurl.js
40	goog.html.safestyle.js
40	goog.dom.safe.js
40	goog.debug.debug.js
40	cljs.tools.reader.impl.utils.js
40	cljs.core.async.impl.buffers.js
32	goog.window.window.js
32	goog.structs.map.js
32	goog.math.rect.js
32	goog.math.math.js
32	goog.json.json.js
32	goog.html.trustedresourceurl.js
32	goog.html.safestylesheet.js
32	goog.functions.functions.js
32	goog.events.eventtarget.js
32	goog.dom.tagname.js
32	goog.dom.forms.js
32	goog.asserts.asserts.js
32	clojure.string.js
32	clojure.set.js
32	clojure.data.js
32	cljs.repl.js
32	cljs.core.async.impl.timers.js
32	cljs.core.async.impl.protocols.js
24	shadow.util.js
24	respo_ui.core.js
24	respo.util.format.js
24	respo.render.patch.js
24	respo.render.dom.js
24	respo.core.js
24	goog.timer.timer.js
24	goog.structs.structs.js
24	goog.math.coordinate.js
24	goog.math.box.js
24	goog.labs.useragent.browser.js
24	goog.html.uncheckedconversions.js
24	goog.html.safescript.js
24	goog.events.listenermap.js
24	goog.events.listenable.js
24	goog.events.eventtype.js
24	goog.events.browserevent.js
24	goog.dom.classlist.js
24	goog.dom.asserts.js
24	goog.disposable.disposable.js
24	goog.async.nexttick.js
24	cljs.tools.reader.impl.inspect.js
24	cljs.tools.reader.impl.commons.js
24	app.comp.container.js
16	respo_message.comp.message.js
16	respo.render.expand.js
16	respo.controller.resolve.js
16	respo.comp.inspect.js
16	recollect.patch.js
16	goog.useragent.product.js
16	goog.style.transition.js
16	goog.string.stringformat.js
16	goog.string.const.js
16	goog.result.simpleresult.js
16	goog.reflect.reflect.js
16	goog.promise.thenable.js
16	goog.net.xmlhttp.js
16	goog.math.size.js
16	goog.log.log.js
16	goog.labs.useragent.util.js
16	goog.labs.useragent.platform.js
16	goog.labs.useragent.engine.js
16	goog.html.legacyconversions.js
16	goog.events.event.js
16	goog.debug.logrecord.js
16	goog.debug.logbuffer.js
16	goog.debug.entrypointregistry.js
16	goog.async.run.js
16	cumulo_reel.comp.reel.js
16	clojure.walk.js
16	app.comp.profile.js
16	app.comp.navigation.js
16	app.comp.login.js
16	app.client.js
8	ws_edn.client.js
8	shadow.module.client.append.js
8	shadow.js.js
8	respo_ui.colors.js
8	respo_message.schema.js
8	respo_message.comp.messages.js
8	respo.util.list.js
8	respo.util.dom.js
8	respo.util.detect.js
8	respo.schema.op.js
8	respo.schema.js
8	respo.env.js
8	respo.cursor.js
8	respo.controller.client.js
8	respo.comp.space.js
8	recollect.util.js
8	recollect.schema.js
8	module$node_modules$shortid$lib$util$cluster_worker_id_browser.js
8	module$node_modules$shortid$lib$random$random_from_seed.js
8	module$node_modules$shortid$lib$random$random_byte_browser.js
8	module$node_modules$shortid$lib$is_valid.js
8	module$node_modules$shortid$lib$index.js
8	module$node_modules$shortid$lib$generate.js
8	module$node_modules$shortid$lib$build.js
8	module$node_modules$shortid$lib$alphabet.js
8	module$node_modules$shortid$index.js
8	module$node_modules$nanoid$format.js
8	hsl.core.js
8	goog.string.typedstring.js
8	goog.string.stringbuffer.js
8	goog.result.result_interface.js
8	goog.result.dependentresult.js
8	goog.promise.resolver.js
8	goog.net.xmlhttpfactory.js
8	goog.net.xhrlike.js
8	goog.net.wrapperxmlhttpfactory.js
8	goog.net.httpstatus.js
8	goog.net.eventtype.js
8	goog.net.errorcode.js
8	goog.math.irect.js
8	goog.json.hybrid.js
8	goog.fs.url.js
8	goog.events.listener.js
8	goog.events.eventid.js
8	goog.events.browserfeature.js
8	goog.dom.vendor.js
8	goog.dom.tags.js
8	goog.dom.nodetype.js
8	goog.dom.inputtype.js
8	goog.dom.htmlelement.js
8	goog.dom.browserfeature.js
8	goog.disposable.idisposable.js
8	goog.debug.errorcontext.js
8	goog.debug.error.js
8	goog.async.workqueue.js
8	goog.async.freelist.js
8	cumulo_util.core.js
8	cljs.user.js
8	cljs.core.async.impl.dispatch.js
8	app.style.js
8	app.schema.js
8	app.config.js
8	app.comp.workspace.js

```

Although it’s cool when I develop my code with `localhost`, when I develop with IP address, it brings problems(not figuring out why, but there are too many requests):

 ![image](https://clojureverse.org/uploads/default/original/2X/1/12c0819863bbe75b2c04089ad48d0aca6e017839.png)

Besides, sometimes I need to run my code in another devices, like phones, pads, the files goes over the network and then it’s loaded. Given the amount of files, it’s less convenient and in some cases it’s scary.

Yeah `:loader-mode eval` can solve major parts of this problem by merging all the files into a single file, then eval it. But there is still some issues that I have to wait and load a large file or use tricks to catch the exceptions.

So I do want to ask if there’s still any chance that we can remove the code we will never use, I guess I will not use `goog.style.transition.js` after it loaded. Then we can reduce the bundle size even in development.

---

<div class="post-metadata">

### Author: ![thheller](https://clojureverse.org/user_avatar/clojureverse.org/thheller/32/534_2.png) [@thheller](https://clojureverse.org/u/thheller)
#### Post date: [January 27, 2019, 5:57am UTC](https://clojureverse.org/t/any-chance-we-could-reduce-the-bundle-size-in-development/3690/2 "2019-01-27T05:57:01Z")

</div>

Short answer: No.

DCE during development is simply not practical. The whole point of development mode is fast feedback which you simply cannot get if you try to analyze the code on every recompile to figure out what is “dead”. Also things that were “dead” at one point may not be later (eg. you add a new function that uses another function that was previously unused). Reconciling that at runtime would be a nightmare.

I also do not see the point in trying to micro optimize out small namespaces like the `goog.style.transition` you mention. It is used, otherwise it would not be loaded. Sure we can try and remove the code that uses it but overall it wouldn’t change much.

Don’t know what is happening with your webserver and why it fails to serve the files? Is this with the built-in shadow-cljs server or something else? I have never seen such issues.

The one thing that could be done to optimize download times is to allow caching, it can greatly improve overall performance if rarely changed files (eg. `cljs.core`) are cached. I would consider adding a flag that lets you disable the “no-cache” policy. I have personally been bitten by too many caching issues in the past so I wouldn’t recommend it but that doesn’t mean we can’t add it.

Another thing you can do is enable https which will make the built-in server switch to http/2 which can also greatly increase performance when many files are involved. You do however get better results overall with `:loader-mode :eval` + gzip.

The last thing you can always do is code-split and delay loading code you don’t actually use initially until you actually do. This is just as relevant during development as it is in production.

`:loader-mode :eval` + gzip has shown the best results in my tests and I have not come up with anything better so far. I’m all ears if you have ideas but DCE is not it.

> [@jiyinyiyong](#):
>
> … use tricks to catch the exceptions.

Not sure what this means.

---

<div class="post-metadata">

### Author: ![jiyinyiyong](https://clojureverse.org/user_avatar/clojureverse.org/jiyinyiyong/32/85_2.png) [@jiyinyiyong](https://clojureverse.org/u/jiyinyiyong)
#### Post date: [January 27, 2019, 4:49pm UTC](https://clojureverse.org/t/any-chance-we-could-reduce-the-bundle-size-in-development/3690/3 "2019-01-27T16:49:31Z")

</div>

> I also do not see the point in trying to micro optimize out small namespaces like the `goog.style.transition` you mention. It is used, otherwise it would not be loaded.

I see so many files from `goog.*` so I think it contains all the files in Closure Library. Any means to tell if it’s really used?

> Don’t know what is happening with your webserver and why it fails to serve the files? Is this with the built-in shadow-cljs server or something else? I have never seen such issues.

I was using the built-in server. The browser is Chrome canary. I debugging the page with `http://<IP_ADDRESS>:7000`. If I visit the page with `http://localhost:7000`, it’s all fine. I guess Chrome is banning some of the requests when there are too many.

> The last thing you can always do is code-split and delay loading code you don’t actually use initially until you actually do. This is just as relevant during development as it is in production.

Not trying that since my own core base is relatively small compared to `cljs.core` and `goog.*`.

> Another thing you can do is enable https which will make the built-in server switch to http/2 which can also greatly increase performance when many files are involved.

HTTP2 for files also means WSS for WebSockets. I have to think about that. It’s a solution but not very easy for me…

> You do however get better results overall with `:loader-mode :eval` + gzip.

Agreed. It’s the best solution at current. `5M` is large, but I can’t say it’s too slow over local network.

> … use tricks to catch the exceptions.

When code is running in `:eval` mode, exceptions are caught, which means my breakpoints are not taking effects as usual. I can still turn on “Pause on caught exceptions”, just less easier to get the specific exception.

---

<div class="post-metadata">

### Author: ![thheller](https://clojureverse.org/user_avatar/clojureverse.org/thheller/32/534_2.png) [@thheller](https://clojureverse.org/u/thheller)
#### Post date: [January 27, 2019, 9:42pm UTC](https://clojureverse.org/t/any-chance-we-could-reduce-the-bundle-size-in-development/3690/4 "2019-01-27T21:42:36Z")

</div>

> [@jiyinyiyong](#):
>
> I see so many files from `goog.*` so I think it contains all the files in Closure Library. Any means to tell if it’s really used?

shadow-cljs only adds files to builds that were actually required somewhere. Whether or not something from those files is actually used is difficult to tell but they definitely were required by something. For example the `goog.style.transition` ends up included because of

```auto
entry shadow.cljs.devtools.client.browser
requires -> shadow.cljs.devtools.client.hud
requires -> shadow.dom
requires -> goog.style.transition

```

(I’m trying to find a good visualization for this info so you can quickly tell why something was included in a build. A rough preview will probably be included in the next version).

> [@jiyinyiyong](#):
>
> I guess Chrome is banning some of the requests when there are too many.

I have never seen this issue even in test builds that had 3000+ included files. Do you have any plugins/extensions which may block those requests? Any configured proxy?

> [@jiyinyiyong](#):
>
> When code is running in `:eval` mode, exceptions are caught, which means my breakpoints are not taking effects as usual.

You mean exceptions thrown while loading the file?

---

<div class="post-metadata">

### Author: ![jiyinyiyong](https://clojureverse.org/user_avatar/clojureverse.org/jiyinyiyong/32/85_2.png) [@jiyinyiyong](https://clojureverse.org/u/jiyinyiyong)
#### Post date: [January 28, 2019, 2:49am UTC](https://clojureverse.org/t/any-chance-we-could-reduce-the-bundle-size-in-development/3690/5 "2019-01-28T02:49:55Z")

</div>

> A rough preview will probably be included in the next version

Sounds cool.

> Do you have any plugins/extensions which may block those requests? Any configured proxy?

I’m using a proxy but the requests to local networks should not using it. I should try with a cleaner environment next I see it.

> You mean exceptions thrown while loading the file?

I think it happened during my interactions with the page, not during loading. (Not very certain.)

---

<div class="post-metadata">

### Author: ![thheller](https://clojureverse.org/user_avatar/clojureverse.org/thheller/32/534_2.png) [@thheller](https://clojureverse.org/u/thheller)
#### Post date: [January 30, 2019, 5:32pm UTC](https://clojureverse.org/t/any-chance-we-could-reduce-the-bundle-size-in-development/3690/6 "2019-01-30T17:32:39Z")

</div>

> [@thheller](#):
>
> The one thing that could be done to optimize download times is to allow caching …

I did some experiments and as of `2.7.22` enabled “soft” caching by default. This means that the browser is still forced to revalidate before using the cache but the server can reply with a “304 Not Modified” to allow skipping the actual download.

Depending on your network configuration and overall build composition this can outperform `:loader-mode :eval`. You may want to experiment a bit with this yourself since the results vary greatly. I still find `:loader-mode :eval` to have the best results in larger builds with lots of files (especially when accessing non-local servers).

Maybe it helps a little bit.

---

<div class="post-metadata">

### Author: ![jiyinyiyong](https://clojureverse.org/user_avatar/clojureverse.org/jiyinyiyong/32/85_2.png) [@jiyinyiyong](https://clojureverse.org/u/jiyinyiyong)
#### Post date: [February 1, 2019, 4:43pm UTC](https://clojureverse.org/t/any-chance-we-could-reduce-the-bundle-size-in-development/3690/7 "2019-02-01T16:43:17Z")

</div>

Not sure if it’s useful but I recorded a video when I see the problem again. It’s fixed after I switched back to `localhost:7000`. [https://youtu.be/ZEE6SHjL-7M](https://youtu.be/ZEE6SHjL-7M)

---

<div class="post-metadata">

### Author: ![thheller](https://clojureverse.org/user_avatar/clojureverse.org/thheller/32/534_2.png) [@thheller](https://clojureverse.org/u/thheller)
#### Post date: [February 1, 2019, 9:30pm UTC](https://clojureverse.org/t/any-chance-we-could-reduce-the-bundle-size-in-development/3690/8 "2019-02-01T21:30:07Z")

</div>

Is that your local IP or something running in a container or so? The server should basically never take that long to answer a file request. I’d suspect something inbetween you and the server. Might be a firewall, proxy, chrome extension, etc, etc …

---

<div class="post-metadata">

### Author: ![thheller](https://clojureverse.org/user_avatar/clojureverse.org/thheller/32/534_2.png) [@thheller](https://clojureverse.org/u/thheller)
#### Post date: [February 2, 2019, 10:33am UTC](https://clojureverse.org/t/any-chance-we-could-reduce-the-bundle-size-in-development/3690/9 "2019-02-02T10:33:10Z")

</div>

Please try using a different webserver and see if it behaves the same. Any other webserver will do but should probably use a fast one (eg. nginx), otherwise it just might naturally throttle due to the webserver answering slowly.

---

<div class="post-metadata">

### Author: ![jiyinyiyong](https://clojureverse.org/user_avatar/clojureverse.org/jiyinyiyong/32/85_2.png) [@jiyinyiyong](https://clojureverse.org/u/jiyinyiyong)
#### Post date: [February 3, 2019, 8:53am UTC](https://clojureverse.org/t/any-chance-we-could-reduce-the-bundle-size-in-development/3690/10 "2019-02-03T08:53:00Z")

</div>

I think it’s not a problem in the webserver. In the video I also tried using `curl` to fetch the file and it worked.

I’m tried `http-server` from npm, it also triggered this problem at times.

---

<div class="post-metadata">

### Author: ![jiyinyiyong](https://clojureverse.org/user_avatar/clojureverse.org/jiyinyiyong/32/85_2.png) [@jiyinyiyong](https://clojureverse.org/u/jiyinyiyong)
#### Post date: [February 4, 2019, 4:57pm UTC](https://clojureverse.org/t/any-chance-we-could-reduce-the-bundle-size-in-development/3690/11 "2019-02-04T16:57:06Z")

</div>

> [@thheller](#):
>
> Any configured proxy?

Tried several times today. Now I think it’s my proxy causing the problem.

---

<div class="post-metadata">

### Author: ![jiyinyiyong](https://clojureverse.org/user_avatar/clojureverse.org/jiyinyiyong/32/85_2.png) [@jiyinyiyong](https://clojureverse.org/u/jiyinyiyong)
#### Post date: [August 6, 2019, 4:57am UTC](https://clojureverse.org/t/any-chance-we-could-reduce-the-bundle-size-in-development/3690/12 "2019-08-06T04:57:09Z")

</div>

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.
